0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060 my $start = 0;
0061 my $val = "";
0062
0063 my $pwd = `pwd`;
0064 chomp $pwd;
0065 my $tree = $pwd;
0066 my $build;
0067
0068 my $output_config;
0069 my $reset_bisect;
0070
0071 sub usage {
0072 print << "EOF"
0073
0074 usage: config-bisect.pl [-l linux-tree][-b build-dir] good-config bad-config [good|bad]
0075 -l [optional] define location of linux-tree (default is current directory)
0076 -b [optional] define location to build (O=build-dir) (default is linux-tree)
0077 good-config the config that is considered good
0078 bad-config the config that does not work
0079 "good" add this if the last run produced a good config
0080 "bad" add this if the last run produced a bad config
0081 If "good" or "bad" is not specified, then it is the start of a new bisect
0082
0083 Note, each run will create copy of good and bad configs with ".tmp" appended.
0084
0085 EOF
0086 ;
0087
0088 exit(-1);
0089 }
0090
0091 sub doprint {
0092 print @_;
0093 }
0094
0095 sub dodie {
0096 doprint "CRITICAL FAILURE... ", @_, "\n";
0097
0098 die @_, "\n";
0099 }
0100
0101 sub expand_path {
0102 my ($file) = @_;
0103
0104 if ($file =~ ) {
0105 return $file;
0106 }
0107 return "$pwd/$file";
0108 }
0109
0110 sub read_prompt {
0111 my ($cancel, $prompt) = @_;
0112
0113 my $ans;
0114
0115 for (;;) {
0116 if ($cancel) {
0117 print "$prompt [y/n/C] ";
0118 } else {
0119 print "$prompt [y/N] ";
0120 }
0121 $ans = <STDIN>;
0122 chomp $ans;
0123 if ($ans =~ /^\s*$/) {
0124 if ($cancel) {
0125 $ans = "c";
0126 } else {
0127 $ans = "n";
0128 }
0129 }
0130 last if ($ans =~ /^y$/i || $ans =~ /^n$/i);
0131 if ($cancel) {
0132 last if ($ans =~ /^c$/i);
0133 print "Please answer either 'y', 'n' or 'c'.\n";
0134 } else {
0135 print "Please answer either 'y' or 'n'.\n";
0136 }
0137 }
0138 if ($ans =~ /^c/i) {
0139 exit;
0140 }
0141 if ($ans !~ /^y$/i) {
0142 return 0;
0143 }
0144 return 1;
0145 }
0146
0147 sub read_yn {
0148 my ($prompt) = @_;
0149
0150 return read_prompt 0, $prompt;
0151 }
0152
0153 sub read_ync {
0154 my ($prompt) = @_;
0155
0156 return read_prompt 1, $prompt;
0157 }
0158
0159 sub run_command {
0160 my ($command, $redirect) = @_;
0161 my $start_time;
0162 my $end_time;
0163 my $dord = 0;
0164 my $pid;
0165
0166 $start_time = time;
0167
0168 doprint("$command ... ");
0169
0170 $pid = open(CMD, "$command 2>&1 |") or
0171 dodie "unable to exec $command";
0172
0173 if (defined($redirect)) {
0174 open (RD, ">$redirect") or
0175 dodie "failed to write to redirect $redirect";
0176 $dord = 1;
0177 }
0178
0179 while (<CMD>) {
0180 print RD if ($dord);
0181 }
0182
0183 waitpid($pid, 0);
0184 my $failed = $?;
0185
0186 close(CMD);
0187 close(RD) if ($dord);
0188
0189 $end_time = time;
0190 my $delta = $end_time - $start_time;
0191
0192 if ($delta == 1) {
0193 doprint "[1 second] ";
0194 } else {
0195 doprint "[$delta seconds] ";
0196 }
0197
0198 if ($failed) {
0199 doprint "FAILED!\n";
0200 } else {
0201 doprint "SUCCESS\n";
0202 }
0203
0204 return !$failed;
0205 }
0206
0207
0208
0209
0210
0211
0212 my %config_ignore;
0213
0214
0215 my %config_set;
0216
0217
0218
0219
0220 my %config_off;
0221
0222
0223 my @config_off_tmp;
0224
0225
0226 my %config_list;
0227 my %null_config;
0228
0229 my %dependency;
0230
0231 my $make;
0232
0233 sub make_oldconfig {
0234
0235 if (!run_command "$make olddefconfig") {
0236
0237
0238 doprint "olddefconfig failed, trying make oldnoconfig\n";
0239 if (!run_command "$make oldnoconfig") {
0240 doprint "oldnoconfig failed, trying yes '' | make oldconfig\n";
0241
0242 run_command "yes '' | $make oldconfig" or
0243 dodie "failed make config oldconfig";
0244 }
0245 }
0246 }
0247
0248 sub assign_configs {
0249 my ($hash, $config) = @_;
0250
0251 doprint "Reading configs from $config\n";
0252
0253 open (IN, $config)
0254 or dodie "Failed to read $config";
0255
0256 while (<IN>) {
0257 chomp;
0258 if (/^((CONFIG\S*)=.*)/) {
0259 ${$hash}{$2} = $1;
0260 } elsif (/^(
0261 ${$hash}{$2} = $1;
0262 }
0263 }
0264
0265 close(IN);
0266 }
0267
0268 sub process_config_ignore {
0269 my ($config) = @_;
0270
0271 assign_configs \%config_ignore, $config;
0272 }
0273
0274 sub get_dependencies {
0275 my ($config) = @_;
0276
0277 my $arr = $dependency{$config};
0278 if (!defined($arr)) {
0279 return ();
0280 }
0281
0282 my @deps = @{$arr};
0283
0284 foreach my $dep (@{$arr}) {
0285 print "ADD DEP $dep\n";
0286 @deps = (@deps, get_dependencies $dep);
0287 }
0288
0289 return @deps;
0290 }
0291
0292 sub save_config {
0293 my ($pc, $file) = @_;
0294
0295 my %configs = %{$pc};
0296
0297 doprint "Saving configs into $file\n";
0298
0299 open(OUT, ">$file") or dodie "Can not write to $file";
0300
0301 foreach my $config (keys %configs) {
0302 print OUT "$configs{$config}\n";
0303 }
0304 close(OUT);
0305 }
0306
0307 sub create_config {
0308 my ($name, $pc) = @_;
0309
0310 doprint "Creating old config from $name configs\n";
0311
0312 save_config $pc, $output_config;
0313
0314 make_oldconfig;
0315 }
0316
0317
0318
0319 sub diff_config_vals {
0320 my ($pa, $pb) = @_;
0321
0322
0323 my %a = %{$pa};
0324 my %b = %{$pb};
0325
0326 my %ret;
0327
0328 foreach my $item (keys %a) {
0329 if (defined($b{$item}) && $b{$item} ne $a{$item}) {
0330 $ret{$item} = $b{$item};
0331 }
0332 }
0333
0334 return %ret;
0335 }
0336
0337
0338 sub diff_configs {
0339 my ($pa, $pb) = @_;
0340
0341 my %ret;
0342
0343
0344 my %a = %{$pa};
0345 my %b = %{$pb};
0346
0347 foreach my $item (keys %b) {
0348 if (!defined($a{$item})) {
0349 $ret{$item} = $b{$item};
0350 }
0351 }
0352
0353 return %ret;
0354 }
0355
0356
0357
0358
0359
0360 sub compare_configs {
0361 my ($pa, $pb) = @_;
0362
0363 my %ret;
0364
0365
0366 my %a = %{$pa};
0367 my %b = %{$pb};
0368
0369 foreach my $item (keys %b) {
0370 if (!defined($a{$item})) {
0371 return 1;
0372 }
0373 if ($a{$item} ne $b{$item}) {
0374 return 1;
0375 }
0376 }
0377
0378 foreach my $item (keys %a) {
0379 if (!defined($b{$item})) {
0380 return -1;
0381 }
0382 }
0383
0384 return 0;
0385 }
0386
0387 sub process_failed {
0388 my ($config) = @_;
0389
0390 doprint "\n\n***************************************\n";
0391 doprint "Found bad config: $config\n";
0392 doprint "***************************************\n\n";
0393 }
0394
0395 sub process_new_config {
0396 my ($tc, $nc, $gc, $bc) = @_;
0397
0398 my %tmp_config = %{$tc};
0399 my %good_configs = %{$gc};
0400 my %bad_configs = %{$bc};
0401
0402 my %new_configs;
0403
0404 my $runtest = 1;
0405 my $ret;
0406
0407 create_config "tmp_configs", \%tmp_config;
0408 assign_configs \%new_configs, $output_config;
0409
0410 $ret = compare_configs \%new_configs, \%bad_configs;
0411 if (!$ret) {
0412 doprint "New config equals bad config, try next test\n";
0413 $runtest = 0;
0414 }
0415
0416 if ($runtest) {
0417 $ret = compare_configs \%new_configs, \%good_configs;
0418 if (!$ret) {
0419 doprint "New config equals good config, try next test\n";
0420 $runtest = 0;
0421 }
0422 }
0423
0424 %{$nc} = %new_configs;
0425
0426 return $runtest;
0427 }
0428
0429 sub convert_config {
0430 my ($config) = @_;
0431
0432 if ($config =~ /^
0433 $config = "$1=n";
0434 }
0435
0436 $config =~ s/^CONFIG_//;
0437 return $config;
0438 }
0439
0440 sub print_config {
0441 my ($sym, $config) = @_;
0442
0443 $config = convert_config $config;
0444 doprint "$sym$config\n";
0445 }
0446
0447 sub print_config_compare {
0448 my ($good_config, $bad_config) = @_;
0449
0450 $good_config = convert_config $good_config;
0451 $bad_config = convert_config $bad_config;
0452
0453 my $good_value = $good_config;
0454 my $bad_value = $bad_config;
0455 $good_value =~ s/(.*)=//;
0456 my $config = $1;
0457
0458 $bad_value =~ s/.*=//;
0459
0460 doprint " $config $good_value -> $bad_value\n";
0461 }
0462
0463
0464
0465
0466
0467
0468
0469 sub make_half {
0470 my ($phalf, $oconfigs, $sconfigs, $which, $type) = @_;
0471
0472 my @half = @{$phalf};
0473 my %orig_configs = %{$oconfigs};
0474 my %source_configs = %{$sconfigs};
0475
0476 my %tmp_config = %orig_configs;
0477
0478 doprint "Settings bisect with $which half of $type configs:\n";
0479 foreach my $item (@half) {
0480 doprint "Updating $item to $source_configs{$item}\n";
0481 $tmp_config{$item} = $source_configs{$item};
0482 }
0483
0484 return %tmp_config;
0485 }
0486
0487 sub run_config_bisect {
0488 my ($pgood, $pbad) = @_;
0489
0490 my %good_configs = %{$pgood};
0491 my %bad_configs = %{$pbad};
0492
0493 my %diff_configs = diff_config_vals \%good_configs, \%bad_configs;
0494 my %b_configs = diff_configs \%good_configs, \%bad_configs;
0495 my %g_configs = diff_configs \%bad_configs, \%good_configs;
0496
0497
0498 my @diff_arr = keys %diff_configs;
0499 my $len_diff = $#diff_arr + 1;
0500
0501
0502 my @b_arr = keys %b_configs;
0503 my $len_b = $#b_arr + 1;
0504
0505
0506 my @g_arr = keys %g_configs;
0507 my $len_g = $#g_arr + 1;
0508
0509 my $runtest = 0;
0510 my %new_configs;
0511 my $ret;
0512
0513
0514
0515
0516
0517
0518
0519 doprint "# of configs to check: $len_diff\n";
0520 doprint "# of configs showing only in good: $len_g\n";
0521 doprint "# of configs showing only in bad: $len_b\n";
0522
0523 if ($len_diff > 0) {
0524
0525
0526 doprint "Configs left to check:\n";
0527 doprint " Good Config\t\t\tBad Config\n";
0528 doprint " -----------\t\t\t----------\n";
0529 foreach my $item (@diff_arr) {
0530 doprint " $good_configs{$item}\t$bad_configs{$item}\n";
0531 }
0532
0533 my $half = int($#diff_arr / 2);
0534 my @tophalf = @diff_arr[0 .. $half];
0535
0536 doprint "Set tmp config to be good config with some bad config values\n";
0537
0538 my %tmp_config = make_half \@tophalf, \%good_configs,
0539 \%bad_configs, "top", "bad";
0540
0541 $runtest = process_new_config \%tmp_config, \%new_configs,
0542 \%good_configs, \%bad_configs;
0543
0544 if (!$runtest) {
0545 doprint "Set tmp config to be bad config with some good config values\n";
0546
0547 my %tmp_config = make_half \@tophalf, \%bad_configs,
0548 \%good_configs, "top", "good";
0549
0550 $runtest = process_new_config \%tmp_config, \%new_configs,
0551 \%good_configs, \%bad_configs;
0552 }
0553 }
0554
0555 if (!$runtest && $len_diff > 0) {
0556
0557
0558 my $half = int($#diff_arr / 2);
0559 my @bottomhalf = @diff_arr[$half+1 .. $#diff_arr];
0560
0561 doprint "Set tmp config to be good config with some bad config values\n";
0562
0563 my %tmp_config = make_half \@bottomhalf, \%good_configs,
0564 \%bad_configs, "bottom", "bad";
0565
0566 $runtest = process_new_config \%tmp_config, \%new_configs,
0567 \%good_configs, \%bad_configs;
0568
0569 if (!$runtest) {
0570 doprint "Set tmp config to be bad config with some good config values\n";
0571
0572 my %tmp_config = make_half \@bottomhalf, \%bad_configs,
0573 \%good_configs, "bottom", "good";
0574
0575 $runtest = process_new_config \%tmp_config, \%new_configs,
0576 \%good_configs, \%bad_configs;
0577 }
0578 }
0579
0580 if ($runtest) {
0581 make_oldconfig;
0582 doprint "READY TO TEST .config IN $build\n";
0583 return 0;
0584 }
0585
0586 doprint "\n%%%%%%%% FAILED TO FIND SINGLE BAD CONFIG %%%%%%%%\n";
0587 doprint "Hmm, can't make any more changes without making good == bad?\n";
0588 doprint "Difference between good (+) and bad (-)\n";
0589
0590 foreach my $item (keys %bad_configs) {
0591 if (!defined($good_configs{$item})) {
0592 print_config "-", $bad_configs{$item};
0593 }
0594 }
0595
0596 foreach my $item (keys %good_configs) {
0597 next if (!defined($bad_configs{$item}));
0598 if ($good_configs{$item} ne $bad_configs{$item}) {
0599 print_config_compare $good_configs{$item}, $bad_configs{$item};
0600 }
0601 }
0602
0603 foreach my $item (keys %good_configs) {
0604 if (!defined($bad_configs{$item})) {
0605 print_config "+", $good_configs{$item};
0606 }
0607 }
0608 return -1;
0609 }
0610
0611 sub config_bisect {
0612 my ($good_config, $bad_config) = @_;
0613 my $ret;
0614
0615 my %good_configs;
0616 my %bad_configs;
0617 my %tmp_configs;
0618
0619 doprint "Run good configs through make oldconfig\n";
0620 assign_configs \%tmp_configs, $good_config;
0621 create_config "$good_config", \%tmp_configs;
0622 assign_configs \%good_configs, $output_config;
0623
0624 doprint "Run bad configs through make oldconfig\n";
0625 assign_configs \%tmp_configs, $bad_config;
0626 create_config "$bad_config", \%tmp_configs;
0627 assign_configs \%bad_configs, $output_config;
0628
0629 save_config \%good_configs, $good_config;
0630 save_config \%bad_configs, $bad_config;
0631
0632 return run_config_bisect \%good_configs, \%bad_configs;
0633 }
0634
0635 while ($#ARGV >= 0) {
0636 if ($ARGV[0] !~ ) {
0637 last;
0638 }
0639 my $opt = shift @ARGV;
0640
0641 if ($opt eq "-b") {
0642 $val = shift @ARGV;
0643 if (!defined($val)) {
0644 die "-b requires value\n";
0645 }
0646 $build = $val;
0647 }
0648
0649 elsif ($opt eq "-l") {
0650 $val = shift @ARGV;
0651 if (!defined($val)) {
0652 die "-l requires value\n";
0653 }
0654 $tree = $val;
0655 }
0656
0657 elsif ($opt eq "-r") {
0658 $reset_bisect = 1;
0659 }
0660
0661 elsif ($opt eq "-h") {
0662 usage;
0663 }
0664
0665 else {
0666 die "Unknown option $opt\n";
0667 }
0668 }
0669
0670 $build = $tree if (!defined($build));
0671
0672 $tree = expand_path $tree;
0673 $build = expand_path $build;
0674
0675 if ( ! -d $tree ) {
0676 die "$tree not a directory\n";
0677 }
0678
0679 if ( ! -d $build ) {
0680 die "$build not a directory\n";
0681 }
0682
0683 usage if $#ARGV < 1;
0684
0685 if ($#ARGV == 1) {
0686 $start = 1;
0687 } elsif ($#ARGV == 2) {
0688 $val = $ARGV[2];
0689 if ($val ne "good" && $val ne "bad") {
0690 die "Unknown command '$val', bust be either \"good\" or \"bad\"\n";
0691 }
0692 } else {
0693 usage;
0694 }
0695
0696 my $good_start = expand_path $ARGV[0];
0697 my $bad_start = expand_path $ARGV[1];
0698
0699 my $good = "$good_start.tmp";
0700 my $bad = "$bad_start.tmp";
0701
0702 $make = "make";
0703
0704 if ($build ne $tree) {
0705 $make = "make O=$build"
0706 }
0707
0708 $output_config = "$build/.config";
0709
0710 if ($start) {
0711 if ( ! -f $good_start ) {
0712 die "$good_start not found\n";
0713 }
0714 if ( ! -f $bad_start ) {
0715 die "$bad_start not found\n";
0716 }
0717 if ( -f $good || -f $bad ) {
0718 my $p = "";
0719
0720 if ( -f $good ) {
0721 $p = "$good exists\n";
0722 }
0723
0724 if ( -f $bad ) {
0725 $p = "$p$bad exists\n";
0726 }
0727
0728 if (!defined($reset_bisect)) {
0729 if (!read_yn "${p}Overwrite and start new bisect anyway?") {
0730 exit (-1);
0731 }
0732 }
0733 }
0734 run_command "cp $good_start $good" or die "failed to copy to $good\n";
0735 run_command "cp $bad_start $bad" or die "failed to copy to $bad\n";
0736 } else {
0737 if ( ! -f $good ) {
0738 die "Can not find file $good\n";
0739 }
0740 if ( ! -f $bad ) {
0741 die "Can not find file $bad\n";
0742 }
0743 if ($val eq "good") {
0744 run_command "cp $output_config $good" or die "failed to copy $config to $good\n";
0745 } elsif ($val eq "bad") {
0746 run_command "cp $output_config $bad" or die "failed to copy $config to $bad\n";
0747 }
0748 }
0749
0750 chdir $tree || die "can't change directory to $tree";
0751
0752 my $ret = config_bisect $good, $bad;
0753
0754 if (!$ret) {
0755 exit(0);
0756 }
0757
0758 if ($ret > 0) {
0759 doprint "Cleaning temp files\n";
0760 run_command "rm $good";
0761 run_command "rm $bad";
0762 exit(1);
0763 } else {
0764 doprint "See good and bad configs for details:\n";
0765 doprint "good: $good\n";
0766 doprint "bad: $bad\n";
0767 doprint "%%%%%%%% FAILED TO FIND SINGLE BAD CONFIG %%%%%%%%\n";
0768 }
0769 exit(2);