xref: /OK3568_Linux_fs/kernel/tools/testing/ktest/config-bisect.pl (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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