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