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