ktest: Reboot to good kernel after every bisect run
[deliverable/linux.git] / tools / testing / ktest / ktest.pl
CommitLineData
2545eb61 1#!/usr/bin/perl -w
d6ce2a0b 2#
cce1dac8 3# Copyright 2010 - Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
d6ce2a0b
SR
4# Licensed under the terms of the GNU GPL License version 2
5#
2545eb61
SR
6
7use strict;
8use IPC::Open2;
9use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
7faafbd6
SR
10use File::Path qw(mkpath);
11use File::Copy qw(cp);
2545eb61
SR
12use FileHandle;
13
e48c5293
SR
14my $VERSION = "0.2";
15
2545eb61
SR
16$| = 1;
17
18my %opt;
a57419b3
SR
19my %repeat_tests;
20my %repeats;
a75fecec 21my %default;
2545eb61
SR
22
23#default opts
a57419b3 24$default{"NUM_TESTS"} = 1;
a75fecec
SR
25$default{"REBOOT_TYPE"} = "grub";
26$default{"TEST_TYPE"} = "test";
27$default{"BUILD_TYPE"} = "randconfig";
28$default{"MAKE_CMD"} = "make";
29$default{"TIMEOUT"} = 120;
a57419b3 30$default{"TMP_DIR"} = "/tmp/ktest";
a75fecec
SR
31$default{"SLEEP_TIME"} = 60; # sleep time between tests
32$default{"BUILD_NOCLEAN"} = 0;
33$default{"REBOOT_ON_ERROR"} = 0;
34$default{"POWEROFF_ON_ERROR"} = 0;
35$default{"REBOOT_ON_SUCCESS"} = 1;
36$default{"POWEROFF_ON_SUCCESS"} = 0;
37$default{"BUILD_OPTIONS"} = "";
38$default{"BISECT_SLEEP_TIME"} = 60; # sleep time between bisects
39$default{"CLEAR_LOG"} = 0;
c960bb9f 40$default{"BISECT_MANUAL"} = 0;
c23dca7c 41$default{"BISECT_SKIP"} = 1;
a75fecec
SR
42$default{"SUCCESS_LINE"} = "login:";
43$default{"BOOTED_TIMEOUT"} = 1;
44$default{"DIE_ON_FAILURE"} = 1;
e48c5293
SR
45$default{"SSH_EXEC"} = "ssh \$SSH_USER\@\$MACHINE \$SSH_COMMAND";
46$default{"SCP_TO_TARGET"} = "scp \$SRC_FILE \$SSH_USER\@\$MACHINE:\$DST_FILE";
47$default{"REBOOT"} = "ssh \$SSH_USER\@\$MACHINE reboot";
1c8a617a
SR
48$default{"STOP_AFTER_SUCCESS"} = 10;
49$default{"STOP_AFTER_FAILURE"} = 60;
2d01b26a 50$default{"STOP_TEST_AFTER"} = 600;
8d1491ba 51$default{"LOCALVERSION"} = "-test";
2545eb61 52
8d1491ba 53my $ktest_config;
2545eb61 54my $version;
a75fecec 55my $machine;
e48c5293 56my $ssh_user;
a75fecec
SR
57my $tmpdir;
58my $builddir;
59my $outputdir;
51ad1dd1 60my $output_config;
a75fecec 61my $test_type;
7faafbd6 62my $build_type;
a75fecec
SR
63my $build_options;
64my $reboot_type;
65my $reboot_script;
66my $power_cycle;
e48c5293 67my $reboot;
a75fecec
SR
68my $reboot_on_error;
69my $poweroff_on_error;
70my $die_on_failure;
576f627c
SR
71my $powercycle_after_reboot;
72my $poweroff_after_halt;
e48c5293
SR
73my $ssh_exec;
74my $scp_to_target;
a75fecec
SR
75my $power_off;
76my $grub_menu;
2545eb61
SR
77my $grub_number;
78my $target;
79my $make;
8b37ca8c 80my $post_install;
5c42fc5b 81my $noclean;
5f9b6ced 82my $minconfig;
2b7d9b21 83my $addconfig;
5f9b6ced
SR
84my $in_bisect = 0;
85my $bisect_bad = "";
d6ce2a0b 86my $reverse_bisect;
c960bb9f 87my $bisect_manual;
c23dca7c 88my $bisect_skip;
6c5ee0be 89my $in_patchcheck = 0;
5a391fbf 90my $run_test;
6c5ee0be 91my $redirect;
7faafbd6
SR
92my $buildlog;
93my $dmesg;
94my $monitor_fp;
95my $monitor_pid;
96my $monitor_cnt = 0;
a75fecec
SR
97my $sleep_time;
98my $bisect_sleep_time;
99my $store_failures;
100my $timeout;
101my $booted_timeout;
102my $console;
103my $success_line;
1c8a617a
SR
104my $stop_after_success;
105my $stop_after_failure;
2d01b26a 106my $stop_test_after;
a75fecec
SR
107my $build_target;
108my $target_image;
109my $localversion;
576f627c 110my $iteration = 0;
e48c5293 111my $successes = 0;
2545eb61 112
8d1491ba
SR
113my %entered_configs;
114my %config_help;
115
116$config_help{"MACHINE"} = << "EOF"
117 The machine hostname that you will test.
118EOF
119 ;
120$config_help{"SSH_USER"} = << "EOF"
121 The box is expected to have ssh on normal bootup, provide the user
122 (most likely root, since you need privileged operations)
123EOF
124 ;
125$config_help{"BUILD_DIR"} = << "EOF"
126 The directory that contains the Linux source code (full path).
127EOF
128 ;
129$config_help{"OUTPUT_DIR"} = << "EOF"
130 The directory that the objects will be built (full path).
131 (can not be same as BUILD_DIR)
132EOF
133 ;
134$config_help{"BUILD_TARGET"} = << "EOF"
135 The location of the compiled file to copy to the target.
136 (relative to OUTPUT_DIR)
137EOF
138 ;
139$config_help{"TARGET_IMAGE"} = << "EOF"
140 The place to put your image on the test machine.
141EOF
142 ;
143$config_help{"POWER_CYCLE"} = << "EOF"
144 A script or command to reboot the box.
145
146 Here is a digital loggers power switch example
147 POWER_CYCLE = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin\@power/outlet?5=CCL'
148
149 Here is an example to reboot a virtual box on the current host
150 with the name "Guest".
151 POWER_CYCLE = virsh destroy Guest; sleep 5; virsh start Guest
152EOF
153 ;
154$config_help{"CONSOLE"} = << "EOF"
155 The script or command that reads the console
156
157 If you use ttywatch server, something like the following would work.
158CONSOLE = nc -d localhost 3001
159
160 For a virtual machine with guest name "Guest".
161CONSOLE = virsh console Guest
162EOF
163 ;
164$config_help{"LOCALVERSION"} = << "EOF"
165 Required version ending to differentiate the test
166 from other linux builds on the system.
167EOF
168 ;
169$config_help{"REBOOT_TYPE"} = << "EOF"
170 Way to reboot the box to the test kernel.
171 Only valid options so far are "grub" and "script".
172
173 If you specify grub, it will assume grub version 1
174 and will search in /boot/grub/menu.lst for the title \$GRUB_MENU
175 and select that target to reboot to the kernel. If this is not
176 your setup, then specify "script" and have a command or script
177 specified in REBOOT_SCRIPT to boot to the target.
178
179 The entry in /boot/grub/menu.lst must be entered in manually.
180 The test will not modify that file.
181EOF
182 ;
183$config_help{"GRUB_MENU"} = << "EOF"
184 The grub title name for the test kernel to boot
185 (Only mandatory if REBOOT_TYPE = grub)
186
187 Note, ktest.pl will not update the grub menu.lst, you need to
188 manually add an option for the test. ktest.pl will search
189 the grub menu.lst for this option to find what kernel to
190 reboot into.
191
192 For example, if in the /boot/grub/menu.lst the test kernel title has:
193 title Test Kernel
194 kernel vmlinuz-test
195 GRUB_MENU = Test Kernel
196EOF
197 ;
198$config_help{"REBOOT_SCRIPT"} = << "EOF"
199 A script to reboot the target into the test kernel
200 (Only mandatory if REBOOT_TYPE = script)
201EOF
202 ;
203
204
205sub get_ktest_config {
206 my ($config) = @_;
207
208 return if (defined($opt{$config}));
209
210 if (defined($config_help{$config})) {
211 print "\n";
212 print $config_help{$config};
213 }
214
215 for (;;) {
216 print "$config = ";
217 if (defined($default{$config})) {
218 print "\[$default{$config}\] ";
219 }
220 $entered_configs{$config} = <STDIN>;
221 $entered_configs{$config} =~ s/^\s*(.*\S)\s*$/$1/;
222 if ($entered_configs{$config} =~ /^\s*$/) {
223 if ($default{$config}) {
224 $entered_configs{$config} = $default{$config};
225 } else {
226 print "Your answer can not be blank\n";
227 next;
228 }
229 }
230 last;
231 }
232}
233
234sub get_ktest_configs {
235 get_ktest_config("MACHINE");
236 get_ktest_config("SSH_USER");
237 get_ktest_config("BUILD_DIR");
238 get_ktest_config("OUTPUT_DIR");
239 get_ktest_config("BUILD_TARGET");
240 get_ktest_config("TARGET_IMAGE");
241 get_ktest_config("POWER_CYCLE");
242 get_ktest_config("CONSOLE");
243 get_ktest_config("LOCALVERSION");
244
245 my $rtype = $opt{"REBOOT_TYPE"};
246
247 if (!defined($rtype)) {
248 if (!defined($opt{"GRUB_MENU"})) {
249 get_ktest_config("REBOOT_TYPE");
250 $rtype = $entered_configs{"REBOOT_TYPE"};
251 } else {
252 $rtype = "grub";
253 }
254 }
255
256 if ($rtype eq "grub") {
257 get_ktest_config("GRUB_MENU");
258 } else {
259 get_ktest_config("REBOOT_SCRIPT");
260 }
261}
262
a57419b3
SR
263sub set_value {
264 my ($lvalue, $rvalue) = @_;
265
266 if (defined($opt{$lvalue})) {
267 die "Error: Option $lvalue defined more than once!\n";
268 }
21a9679f
SR
269 if ($rvalue =~ /^\s*$/) {
270 delete $opt{$lvalue};
271 } else {
272 $opt{$lvalue} = $rvalue;
273 }
a57419b3
SR
274}
275
2545eb61
SR
276sub read_config {
277 my ($config) = @_;
278
279 open(IN, $config) || die "can't read file $config";
280
a57419b3
SR
281 my $name = $config;
282 $name =~ s,.*/(.*),$1,;
283
284 my $test_num = 0;
285 my $default = 1;
286 my $repeat = 1;
287 my $num_tests_set = 0;
288 my $skip = 0;
289 my $rest;
290
2545eb61
SR
291 while (<IN>) {
292
293 # ignore blank lines and comments
294 next if (/^\s*$/ || /\s*\#/);
295
a57419b3
SR
296 if (/^\s*TEST_START(.*)/) {
297
298 $rest = $1;
299
300 if ($num_tests_set) {
301 die "$name: $.: Can not specify both NUM_TESTS and TEST_START\n";
302 }
303
304 my $old_test_num = $test_num;
e48c5293 305 my $old_repeat = $repeat;
a57419b3
SR
306
307 $test_num += $repeat;
308 $default = 0;
309 $repeat = 1;
310
311 if ($rest =~ /\s+SKIP(.*)/) {
312 $rest = $1;
313 $skip = 1;
314 } else {
315 $skip = 0;
316 }
317
318 if ($rest =~ /\s+ITERATE\s+(\d+)(.*)$/) {
319 $repeat = $1;
320 $rest = $2;
321 $repeat_tests{"$test_num"} = $repeat;
322 }
323
324 if ($rest =~ /\s+SKIP(.*)/) {
325 $rest = $1;
326 $skip = 1;
327 }
328
329 if ($rest !~ /^\s*$/) {
330 die "$name: $.: Gargbage found after TEST_START\n$_";
331 }
332
333 if ($skip) {
334 $test_num = $old_test_num;
e48c5293 335 $repeat = $old_repeat;
a57419b3
SR
336 }
337
338 } elsif (/^\s*DEFAULTS(.*)$/) {
339 $default = 1;
340
341 $rest = $1;
342
343 if ($rest =~ /\s+SKIP(.*)/) {
344 $rest = $1;
345 $skip = 1;
346 } else {
347 $skip = 0;
348 }
349
350 if ($rest !~ /^\s*$/) {
351 die "$name: $.: Gargbage found after DEFAULTS\n$_";
352 }
353
354 } elsif (/^\s*([A-Z_\[\]\d]+)\s*=\s*(.*?)\s*$/) {
355
356 next if ($skip);
357
2545eb61
SR
358 my $lvalue = $1;
359 my $rvalue = $2;
360
a57419b3
SR
361 if (!$default &&
362 ($lvalue eq "NUM_TESTS" ||
363 $lvalue eq "LOG_FILE" ||
364 $lvalue eq "CLEAR_LOG")) {
365 die "$name: $.: $lvalue must be set in DEFAULTS section\n";
366 }
367
368 if ($lvalue eq "NUM_TESTS") {
369 if ($test_num) {
370 die "$name: $.: Can not specify both NUM_TESTS and TEST_START\n";
371 }
372 if (!$default) {
373 die "$name: $.: NUM_TESTS must be set in default section\n";
374 }
375 $num_tests_set = 1;
376 }
377
378 if ($default || $lvalue =~ /\[\d+\]$/) {
379 set_value($lvalue, $rvalue);
380 } else {
381 my $val = "$lvalue\[$test_num\]";
382 set_value($val, $rvalue);
383
384 if ($repeat > 1) {
385 $repeats{$val} = $repeat;
386 }
a75fecec 387 }
a57419b3
SR
388 } else {
389 die "$name: $.: Garbage found in config\n$_";
2545eb61
SR
390 }
391 }
392
393 close(IN);
a75fecec 394
a57419b3
SR
395 if ($test_num) {
396 $test_num += $repeat - 1;
397 $opt{"NUM_TESTS"} = $test_num;
398 }
399
8d1491ba
SR
400 # make sure we have all mandatory configs
401 get_ktest_configs;
402
a75fecec
SR
403 # set any defaults
404
405 foreach my $default (keys %default) {
406 if (!defined($opt{$default})) {
407 $opt{$default} = $default{$default};
408 }
409 }
2545eb61
SR
410}
411
d1e2f22a 412sub _logit {
2545eb61
SR
413 if (defined($opt{"LOG_FILE"})) {
414 open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}";
415 print OUT @_;
416 close(OUT);
417 }
418}
419
d1e2f22a
SR
420sub logit {
421 if (defined($opt{"LOG_FILE"})) {
422 _logit @_;
423 } else {
424 print @_;
425 }
426}
427
5f9b6ced
SR
428sub doprint {
429 print @_;
d1e2f22a 430 _logit @_;
5f9b6ced
SR
431}
432
7faafbd6
SR
433sub run_command;
434
435sub reboot {
436 # try to reboot normally
e48c5293 437 if (run_command $reboot) {
576f627c
SR
438 if (defined($powercycle_after_reboot)) {
439 sleep $powercycle_after_reboot;
440 run_command "$power_cycle";
441 }
442 } else {
7faafbd6 443 # nope? power cycle it.
a75fecec 444 run_command "$power_cycle";
7faafbd6
SR
445 }
446}
447
576f627c
SR
448sub do_not_reboot {
449 my $i = $iteration;
450
451 return $test_type eq "build" ||
452 ($test_type eq "patchcheck" && $opt{"PATCHCHECK_TYPE[$i]"} eq "build") ||
453 ($test_type eq "bisect" && $opt{"BISECT_TYPE[$i]"} eq "build");
454}
455
5c42fc5b 456sub dodie {
5a391fbf 457 doprint "CRITICAL FAILURE... ", @_, "\n";
5c42fc5b 458
576f627c
SR
459 my $i = $iteration;
460
461 if ($reboot_on_error && !do_not_reboot) {
462
75c3fda7 463 doprint "REBOOTING\n";
7faafbd6 464 reboot;
75c3fda7 465
a75fecec 466 } elsif ($poweroff_on_error && defined($power_off)) {
5c42fc5b 467 doprint "POWERING OFF\n";
a75fecec 468 `$power_off`;
5c42fc5b 469 }
75c3fda7 470
f80802cb
SR
471 if (defined($opt{"LOG_FILE"})) {
472 print " See $opt{LOG_FILE} for more info.\n";
473 }
474
576f627c 475 die @_, "\n";
5c42fc5b
SR
476}
477
7faafbd6
SR
478sub open_console {
479 my ($fp) = @_;
480
481 my $flags;
482
a75fecec
SR
483 my $pid = open($fp, "$console|") or
484 dodie "Can't open console $console";
7faafbd6
SR
485
486 $flags = fcntl($fp, F_GETFL, 0) or
576f627c 487 dodie "Can't get flags for the socket: $!";
7faafbd6 488 $flags = fcntl($fp, F_SETFL, $flags | O_NONBLOCK) or
576f627c 489 dodie "Can't set flags for the socket: $!";
7faafbd6
SR
490
491 return $pid;
492}
493
494sub close_console {
495 my ($fp, $pid) = @_;
496
497 doprint "kill child process $pid\n";
498 kill 2, $pid;
499
500 print "closing!\n";
501 close($fp);
502}
503
504sub start_monitor {
505 if ($monitor_cnt++) {
506 return;
507 }
508 $monitor_fp = \*MONFD;
509 $monitor_pid = open_console $monitor_fp;
a75fecec
SR
510
511 return;
512
513 open(MONFD, "Stop perl from warning about single use of MONFD");
7faafbd6
SR
514}
515
516sub end_monitor {
517 if (--$monitor_cnt) {
518 return;
519 }
520 close_console($monitor_fp, $monitor_pid);
521}
522
523sub wait_for_monitor {
524 my ($time) = @_;
525 my $line;
526
a75fecec 527 doprint "** Wait for monitor to settle down **\n";
7faafbd6
SR
528
529 # read the monitor and wait for the system to calm down
530 do {
531 $line = wait_for_input($monitor_fp, $time);
a75fecec 532 print "$line" if (defined($line));
7faafbd6 533 } while (defined($line));
a75fecec 534 print "** Monitor flushed **\n";
7faafbd6
SR
535}
536
2b7d9b21
SR
537sub fail {
538
a75fecec 539 if ($die_on_failure) {
2b7d9b21
SR
540 dodie @_;
541 }
542
a75fecec 543 doprint "FAILED\n";
7faafbd6 544
576f627c
SR
545 my $i = $iteration;
546
a75fecec 547 # no need to reboot for just building.
576f627c 548 if (!do_not_reboot) {
a75fecec
SR
549 doprint "REBOOTING\n";
550 reboot;
551 start_monitor;
552 wait_for_monitor $sleep_time;
553 end_monitor;
554 }
7faafbd6 555
576f627c
SR
556 doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
557 doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
7a849cd9 558 doprint "KTEST RESULT: TEST $i Failed: ", @_, "\n";
576f627c
SR
559 doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
560 doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
a75fecec
SR
561
562 return 1 if (!defined($store_failures));
7faafbd6
SR
563
564 my @t = localtime;
565 my $date = sprintf "%04d%02d%02d%02d%02d%02d",
566 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0];
567
cccae1a6
SR
568 my $type = $build_type;
569 if ($type =~ /useconfig/) {
570 $type = "useconfig";
571 }
572
573 my $dir = "$machine-$test_type-$type-fail-$date";
a75fecec 574 my $faildir = "$store_failures/$dir";
7faafbd6
SR
575
576 if (!-d $faildir) {
577 mkpath($faildir) or
a75fecec 578 die "can't create $faildir";
7faafbd6 579 }
51ad1dd1
SR
580 if (-f "$output_config") {
581 cp "$output_config", "$faildir/config" or
7faafbd6
SR
582 die "failed to copy .config";
583 }
584 if (-f $buildlog) {
585 cp $buildlog, "$faildir/buildlog" or
586 die "failed to move $buildlog";
587 }
588 if (-f $dmesg) {
589 cp $dmesg, "$faildir/dmesg" or
590 die "failed to move $dmesg";
591 }
592
593 doprint "*** Saved info to $faildir ***\n";
594
2b7d9b21
SR
595 return 1;
596}
597
2545eb61
SR
598sub run_command {
599 my ($command) = @_;
d6ce2a0b
SR
600 my $dolog = 0;
601 my $dord = 0;
602 my $pid;
603
e48c5293
SR
604 $command =~ s/\$SSH_USER/$ssh_user/g;
605 $command =~ s/\$MACHINE/$machine/g;
606
d6ce2a0b
SR
607 doprint("$command ... ");
608
609 $pid = open(CMD, "$command 2>&1 |") or
2b7d9b21 610 (fail "unable to exec $command" and return 0);
2545eb61
SR
611
612 if (defined($opt{"LOG_FILE"})) {
d6ce2a0b
SR
613 open(LOG, ">>$opt{LOG_FILE}") or
614 dodie "failed to write to log";
615 $dolog = 1;
6c5ee0be
SR
616 }
617
618 if (defined($redirect)) {
d6ce2a0b
SR
619 open (RD, ">$redirect") or
620 dodie "failed to write to redirect $redirect";
621 $dord = 1;
2545eb61
SR
622 }
623
d6ce2a0b
SR
624 while (<CMD>) {
625 print LOG if ($dolog);
626 print RD if ($dord);
627 }
2545eb61 628
d6ce2a0b 629 waitpid($pid, 0);
2545eb61
SR
630 my $failed = $?;
631
d6ce2a0b
SR
632 close(CMD);
633 close(LOG) if ($dolog);
634 close(RD) if ($dord);
635
2545eb61
SR
636 if ($failed) {
637 doprint "FAILED!\n";
638 } else {
639 doprint "SUCCESS\n";
640 }
641
5f9b6ced
SR
642 return !$failed;
643}
644
e48c5293
SR
645sub run_ssh {
646 my ($cmd) = @_;
647 my $cp_exec = $ssh_exec;
648
649 $cp_exec =~ s/\$SSH_COMMAND/$cmd/g;
650 return run_command "$cp_exec";
651}
652
653sub run_scp {
654 my ($src, $dst) = @_;
655 my $cp_scp = $scp_to_target;
656
657 $cp_scp =~ s/\$SRC_FILE/$src/g;
658 $cp_scp =~ s/\$DST_FILE/$dst/g;
659
660 return run_command "$cp_scp";
661}
662
5f9b6ced
SR
663sub get_grub_index {
664
a75fecec
SR
665 if ($reboot_type ne "grub") {
666 return;
667 }
5a391fbf 668 return if (defined($grub_number));
5f9b6ced
SR
669
670 doprint "Find grub menu ... ";
671 $grub_number = -1;
e48c5293
SR
672
673 my $ssh_grub = $ssh_exec;
674 $ssh_grub =~ s,\$SSH_COMMAND,cat /boot/grub/menu.lst,g;
675
676 open(IN, "$ssh_grub |")
5f9b6ced 677 or die "unable to get menu.lst";
e48c5293 678
5f9b6ced 679 while (<IN>) {
a75fecec 680 if (/^\s*title\s+$grub_menu\s*$/) {
5f9b6ced
SR
681 $grub_number++;
682 last;
683 } elsif (/^\s*title\s/) {
684 $grub_number++;
685 }
686 }
687 close(IN);
688
a75fecec 689 die "Could not find '$grub_menu' in /boot/grub/menu on $machine"
5f9b6ced
SR
690 if ($grub_number < 0);
691 doprint "$grub_number\n";
2545eb61
SR
692}
693
2545eb61
SR
694sub wait_for_input
695{
696 my ($fp, $time) = @_;
697 my $rin;
698 my $ready;
699 my $line;
700 my $ch;
701
702 if (!defined($time)) {
703 $time = $timeout;
704 }
705
706 $rin = '';
707 vec($rin, fileno($fp), 1) = 1;
708 $ready = select($rin, undef, undef, $time);
709
710 $line = "";
711
712 # try to read one char at a time
713 while (sysread $fp, $ch, 1) {
714 $line .= $ch;
715 last if ($ch eq "\n");
716 }
717
718 if (!length($line)) {
719 return undef;
720 }
721
722 return $line;
723}
724
75c3fda7 725sub reboot_to {
a75fecec 726 if ($reboot_type eq "grub") {
eec56460 727 run_ssh "'(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'";
a75fecec
SR
728 return;
729 }
730
731 run_command "$reboot_script";
2545eb61
SR
732}
733
a57419b3
SR
734sub get_sha1 {
735 my ($commit) = @_;
736
737 doprint "git rev-list --max-count=1 $commit ... ";
738 my $sha1 = `git rev-list --max-count=1 $commit`;
739 my $ret = $?;
740
741 logit $sha1;
742
743 if ($ret) {
744 doprint "FAILED\n";
745 dodie "Failed to get git $commit";
746 }
747
748 print "SUCCESS\n";
749
750 chomp $sha1;
751
752 return $sha1;
753}
754
5a391fbf 755sub monitor {
2545eb61
SR
756 my $booted = 0;
757 my $bug = 0;
5c42fc5b 758 my $skip_call_trace = 0;
2b7d9b21 759 my $loops;
2545eb61 760
7faafbd6 761 wait_for_monitor 5;
2545eb61
SR
762
763 my $line;
764 my $full_line = "";
765
7faafbd6
SR
766 open(DMESG, "> $dmesg") or
767 die "unable to write to $dmesg";
2545eb61 768
75c3fda7 769 reboot_to;
2545eb61 770
1c8a617a
SR
771 my $success_start;
772 my $failure_start;
2d01b26a
SR
773 my $monitor_start = time;
774 my $done = 0;
1c8a617a 775
2d01b26a 776 while (!$done) {
2545eb61 777
2b7d9b21 778 if ($booted) {
a75fecec 779 $line = wait_for_input($monitor_fp, $booted_timeout);
2b7d9b21 780 } else {
7faafbd6 781 $line = wait_for_input($monitor_fp);
2b7d9b21 782 }
2545eb61
SR
783
784 last if (!defined($line));
785
786 doprint $line;
7faafbd6 787 print DMESG $line;
2545eb61
SR
788
789 # we are not guaranteed to get a full line
790 $full_line .= $line;
791
a75fecec 792 if ($full_line =~ /$success_line/) {
2545eb61 793 $booted = 1;
1c8a617a
SR
794 $success_start = time;
795 }
796
797 if ($booted && defined($stop_after_success) &&
798 $stop_after_success >= 0) {
799 my $now = time;
800 if ($now - $success_start >= $stop_after_success) {
801 doprint "Test forced to stop after $stop_after_success seconds after success\n";
802 last;
803 }
2545eb61
SR
804 }
805
5c42fc5b
SR
806 if ($full_line =~ /\[ backtrace testing \]/) {
807 $skip_call_trace = 1;
808 }
809
2545eb61 810 if ($full_line =~ /call trace:/i) {
4651920e 811 if (!$bug && !$skip_call_trace) {
1c8a617a
SR
812 $bug = 1;
813 $failure_start = time;
814 }
815 }
816
817 if ($bug && defined($stop_after_failure) &&
818 $stop_after_failure >= 0) {
819 my $now = time;
820 if ($now - $failure_start >= $stop_after_failure) {
821 doprint "Test forced to stop after $stop_after_failure seconds after failure\n";
822 last;
823 }
5c42fc5b
SR
824 }
825
826 if ($full_line =~ /\[ end of backtrace testing \]/) {
827 $skip_call_trace = 0;
828 }
829
830 if ($full_line =~ /Kernel panic -/) {
10abf118 831 $failure_start = time;
2545eb61
SR
832 $bug = 1;
833 }
834
835 if ($line =~ /\n/) {
836 $full_line = "";
837 }
2d01b26a
SR
838
839 if ($stop_test_after > 0 && !$booted && !$bug) {
840 if (time - $monitor_start > $stop_test_after) {
4d62bf51 841 doprint "STOP_TEST_AFTER ($stop_test_after seconds) timed out\n";
2d01b26a
SR
842 $done = 1;
843 }
844 }
2545eb61
SR
845 }
846
7faafbd6 847 close(DMESG);
2545eb61 848
a75fecec 849 if ($bug) {
2b7d9b21 850 return 0 if ($in_bisect);
576f627c 851 fail "failed - got a bug report" and return 0;
2545eb61
SR
852 }
853
a75fecec 854 if (!$booted) {
2b7d9b21 855 return 0 if ($in_bisect);
576f627c 856 fail "failed - never got a boot prompt." and return 0;
2545eb61 857 }
5f9b6ced 858
2b7d9b21 859 return 1;
2545eb61
SR
860}
861
862sub install {
863
e48c5293 864 run_scp "$outputdir/$build_target", "$target_image" or
5c42fc5b 865 dodie "failed to copy image";
2545eb61 866
5f9b6ced 867 my $install_mods = 0;
2545eb61 868
5f9b6ced
SR
869 # should we process modules?
870 $install_mods = 0;
51ad1dd1 871 open(IN, "$output_config") or dodie("Can't read config file");
5f9b6ced
SR
872 while (<IN>) {
873 if (/CONFIG_MODULES(=y)?/) {
874 $install_mods = 1 if (defined($1));
875 last;
5c42fc5b 876 }
5f9b6ced
SR
877 }
878 close(IN);
5c42fc5b 879
5f9b6ced
SR
880 if (!$install_mods) {
881 doprint "No modules needed\n";
882 return;
883 }
2545eb61 884
a75fecec 885 run_command "$make INSTALL_MOD_PATH=$tmpdir modules_install" or
5f9b6ced 886 dodie "Failed to install modules";
5c42fc5b 887
5f9b6ced 888 my $modlib = "/lib/modules/$version";
a57419b3 889 my $modtar = "ktest-mods.tar.bz2";
5c42fc5b 890
e48c5293 891 run_ssh "rm -rf $modlib" or
5f9b6ced 892 dodie "failed to remove old mods: $modlib";
5c42fc5b 893
5f9b6ced 894 # would be nice if scp -r did not follow symbolic links
a75fecec 895 run_command "cd $tmpdir && tar -cjf $modtar lib/modules/$version" or
5f9b6ced
SR
896 dodie "making tarball";
897
e48c5293 898 run_scp "$tmpdir/$modtar", "/tmp" or
5f9b6ced
SR
899 dodie "failed to copy modules";
900
a75fecec 901 unlink "$tmpdir/$modtar";
5f9b6ced 902
e48c5293 903 run_ssh "'(cd / && tar xf /tmp/$modtar)'" or
5f9b6ced 904 dodie "failed to tar modules";
2545eb61 905
e48c5293 906 run_ssh "rm -f /tmp/$modtar";
8b37ca8c
SR
907
908 return if (!defined($post_install));
909
e48c5293 910 my $cp_post_install = $post_install;
ca6a21f8 911 $cp_post_install =~ s/\$KERNEL_VERSION/$version/g;
e48c5293 912 run_command "$cp_post_install" or
576f627c 913 dodie "Failed to run post install";
2545eb61
SR
914}
915
6c5ee0be
SR
916sub check_buildlog {
917 my ($patch) = @_;
918
6c5ee0be
SR
919 my @files = `git show $patch | diffstat -l`;
920
921 open(IN, "git show $patch |") or
922 dodie "failed to show $patch";
923 while (<IN>) {
924 if (m,^--- a/(.*),) {
925 chomp $1;
926 $files[$#files] = $1;
927 }
928 }
929 close(IN);
930
931 open(IN, $buildlog) or dodie "Can't open $buildlog";
932 while (<IN>) {
933 if (/^\s*(.*?):.*(warning|error)/) {
934 my $err = $1;
935 foreach my $file (@files) {
a75fecec 936 my $fullpath = "$builddir/$file";
6c5ee0be 937 if ($file eq $err || $fullpath eq $err) {
2b7d9b21 938 fail "$file built with warnings" and return 0;
6c5ee0be
SR
939 }
940 }
941 }
942 }
943 close(IN);
2b7d9b21
SR
944
945 return 1;
6c5ee0be
SR
946}
947
612b9e9b
SR
948sub make_oldconfig {
949 my ($defconfig) = @_;
950
951 if (!run_command "$defconfig $make oldnoconfig") {
952 # Perhaps oldnoconfig doesn't exist in this version of the kernel
953 # try a yes '' | oldconfig
954 doprint "oldnoconfig failed, trying yes '' | make oldconfig\n";
955 run_command "yes '' | $defconfig $make oldconfig" or
956 dodie "failed make config oldconfig";
957 }
958}
959
2545eb61
SR
960sub build {
961 my ($type) = @_;
5c42fc5b 962 my $defconfig = "";
5c42fc5b 963
7faafbd6
SR
964 unlink $buildlog;
965
75c3fda7 966 if ($type =~ /^useconfig:(.*)/) {
51ad1dd1 967 run_command "cp $1 $output_config" or
75c3fda7 968 dodie "could not copy $1 to .config";
5f9b6ced 969
75c3fda7
SR
970 $type = "oldconfig";
971 }
972
5c42fc5b
SR
973 # old config can ask questions
974 if ($type eq "oldconfig") {
9386c6ab 975 $type = "oldnoconfig";
75c3fda7
SR
976
977 # allow for empty configs
51ad1dd1 978 run_command "touch $output_config";
75c3fda7 979
51ad1dd1 980 run_command "mv $output_config $outputdir/config_temp" or
5c42fc5b 981 dodie "moving .config";
2545eb61 982
5f9b6ced 983 if (!$noclean && !run_command "$make mrproper") {
5c42fc5b
SR
984 dodie "make mrproper";
985 }
2545eb61 986
51ad1dd1 987 run_command "mv $outputdir/config_temp $output_config" or
5c42fc5b 988 dodie "moving config_temp";
5c42fc5b
SR
989
990 } elsif (!$noclean) {
51ad1dd1 991 unlink "$output_config";
5f9b6ced 992 run_command "$make mrproper" or
5c42fc5b 993 dodie "make mrproper";
5c42fc5b 994 }
2545eb61
SR
995
996 # add something to distinguish this build
a75fecec
SR
997 open(OUT, "> $outputdir/localversion") or dodie("Can't make localversion file");
998 print OUT "$localversion\n";
2545eb61
SR
999 close(OUT);
1000
5f9b6ced
SR
1001 if (defined($minconfig)) {
1002 $defconfig = "KCONFIG_ALLCONFIG=$minconfig";
2545eb61
SR
1003 }
1004
612b9e9b
SR
1005 if ($type eq "oldnoconfig") {
1006 make_oldconfig $defconfig;
1007 } else {
1008 run_command "$defconfig $make $type" or
1009 dodie "failed make config";
1010 }
2545eb61 1011
a75fecec
SR
1012 $redirect = "$buildlog";
1013 if (!run_command "$make $build_options") {
6c5ee0be 1014 undef $redirect;
5f9b6ced 1015 # bisect may need this to pass
2b7d9b21
SR
1016 return 0 if ($in_bisect);
1017 fail "failed build" and return 0;
2545eb61 1018 }
6c5ee0be 1019 undef $redirect;
5f9b6ced 1020
2b7d9b21 1021 return 1;
2545eb61
SR
1022}
1023
75c3fda7 1024sub halt {
e48c5293 1025 if (!run_ssh "halt" or defined($power_off)) {
576f627c
SR
1026 if (defined($poweroff_after_halt)) {
1027 sleep $poweroff_after_halt;
1028 run_command "$power_off";
1029 }
1030 } else {
75c3fda7 1031 # nope? the zap it!
a75fecec 1032 run_command "$power_off";
75c3fda7
SR
1033 }
1034}
1035
5f9b6ced
SR
1036sub success {
1037 my ($i) = @_;
1038
e48c5293
SR
1039 $successes++;
1040
5f9b6ced
SR
1041 doprint "\n\n*******************************************\n";
1042 doprint "*******************************************\n";
7a849cd9 1043 doprint "KTEST RESULT: TEST $i SUCCESS!!!! **\n";
5f9b6ced
SR
1044 doprint "*******************************************\n";
1045 doprint "*******************************************\n";
1046
576f627c 1047 if ($i != $opt{"NUM_TESTS"} && !do_not_reboot) {
a75fecec 1048 doprint "Reboot and wait $sleep_time seconds\n";
5f9b6ced 1049 reboot;
7faafbd6 1050 start_monitor;
a75fecec 1051 wait_for_monitor $sleep_time;
7faafbd6 1052 end_monitor;
5f9b6ced
SR
1053 }
1054}
1055
1056sub get_version {
1057 # get the release name
1058 doprint "$make kernelrelease ... ";
1059 $version = `$make kernelrelease | tail -1`;
1060 chomp($version);
1061 doprint "$version\n";
1062}
1063
c960bb9f
SR
1064sub answer_bisect {
1065 for (;;) {
1066 doprint "Pass or fail? [p/f]";
1067 my $ans = <STDIN>;
1068 chomp $ans;
1069 if ($ans eq "p" || $ans eq "P") {
1070 return 1;
1071 } elsif ($ans eq "f" || $ans eq "F") {
1072 return 0;
1073 } else {
1074 print "Please answer 'P' or 'F'\n";
1075 }
1076 }
1077}
1078
5a391fbf 1079sub child_run_test {
7faafbd6 1080 my $failed = 0;
5a391fbf 1081
7faafbd6 1082 # child should have no power
a75fecec
SR
1083 $reboot_on_error = 0;
1084 $poweroff_on_error = 0;
1085 $die_on_failure = 1;
7faafbd6
SR
1086
1087 run_command $run_test or $failed = 1;
5a391fbf
SR
1088 exit $failed;
1089}
1090
1091my $child_done;
1092
1093sub child_finished {
1094 $child_done = 1;
1095}
1096
1097sub do_run_test {
1098 my $child_pid;
1099 my $child_exit;
5a391fbf
SR
1100 my $line;
1101 my $full_line;
1102 my $bug = 0;
5a391fbf 1103
7faafbd6 1104 wait_for_monitor 1;
5a391fbf 1105
7faafbd6 1106 doprint "run test $run_test\n";
5a391fbf
SR
1107
1108 $child_done = 0;
1109
1110 $SIG{CHLD} = qw(child_finished);
1111
1112 $child_pid = fork;
1113
1114 child_run_test if (!$child_pid);
1115
1116 $full_line = "";
1117
1118 do {
7faafbd6 1119 $line = wait_for_input($monitor_fp, 1);
5a391fbf
SR
1120 if (defined($line)) {
1121
1122 # we are not guaranteed to get a full line
1123 $full_line .= $line;
8ea0e063 1124 doprint $line;
5a391fbf
SR
1125
1126 if ($full_line =~ /call trace:/i) {
1127 $bug = 1;
1128 }
1129
1130 if ($full_line =~ /Kernel panic -/) {
1131 $bug = 1;
1132 }
1133
1134 if ($line =~ /\n/) {
1135 $full_line = "";
1136 }
1137 }
1138 } while (!$child_done && !$bug);
1139
1140 if ($bug) {
8ea0e063
SR
1141 my $failure_start = time;
1142 my $now;
1143 do {
1144 $line = wait_for_input($monitor_fp, 1);
1145 if (defined($line)) {
1146 doprint $line;
1147 }
1148 $now = time;
1149 if ($now - $failure_start >= $stop_after_failure) {
1150 last;
1151 }
1152 } while (defined($line));
1153
5a391fbf
SR
1154 doprint "Detected kernel crash!\n";
1155 # kill the child with extreme prejudice
1156 kill 9, $child_pid;
1157 }
1158
1159 waitpid $child_pid, 0;
1160 $child_exit = $?;
1161
5a391fbf 1162 if ($bug || $child_exit) {
2b7d9b21
SR
1163 return 0 if $in_bisect;
1164 fail "test failed" and return 0;
5a391fbf 1165 }
2b7d9b21 1166 return 1;
5a391fbf
SR
1167}
1168
a75fecec
SR
1169sub run_git_bisect {
1170 my ($command) = @_;
1171
1172 doprint "$command ... ";
1173
1174 my $output = `$command 2>&1`;
1175 my $ret = $?;
1176
1177 logit $output;
1178
1179 if ($ret) {
1180 doprint "FAILED\n";
1181 dodie "Failed to git bisect";
1182 }
1183
1184 doprint "SUCCESS\n";
1185 if ($output =~ m/^(Bisecting: .*\(roughly \d+ steps?\))\s+\[([[:xdigit:]]+)\]/) {
1186 doprint "$1 [$2]\n";
1187 } elsif ($output =~ m/^([[:xdigit:]]+) is the first bad commit/) {
1188 $bisect_bad = $1;
1189 doprint "Found bad commit... $1\n";
1190 return 0;
1191 } else {
1192 # we already logged it, just print it now.
1193 print $output;
1194 }
1195
1196 return 1;
1197}
1198
c23dca7c
SR
1199sub bisect_reboot {
1200 doprint "Reboot and sleep $bisect_sleep_time seconds\n";
1201 reboot;
1202 start_monitor;
1203 wait_for_monitor $bisect_sleep_time;
1204 end_monitor;
1205}
1206
1207# returns 1 on success, 0 on failure, -1 on skip
0a05c769
SR
1208sub run_bisect_test {
1209 my ($type, $buildtype) = @_;
5f9b6ced 1210
2b7d9b21 1211 my $failed = 0;
5f9b6ced
SR
1212 my $result;
1213 my $output;
1214 my $ret;
1215
0a05c769
SR
1216 $in_bisect = 1;
1217
1218 build $buildtype or $failed = 1;
5f9b6ced
SR
1219
1220 if ($type ne "build") {
c23dca7c
SR
1221 if ($failed && $bisect_skip) {
1222 $in_bisect = 0;
1223 return -1;
1224 }
7faafbd6 1225 dodie "Failed on build" if $failed;
5f9b6ced
SR
1226
1227 # Now boot the box
1228 get_grub_index;
1229 get_version;
1230 install;
7faafbd6
SR
1231
1232 start_monitor;
2b7d9b21 1233 monitor or $failed = 1;
5f9b6ced
SR
1234
1235 if ($type ne "boot") {
c23dca7c
SR
1236 if ($failed && $bisect_skip) {
1237 end_monitor;
1238 bisect_reboot;
1239 $in_bisect = 0;
1240 return -1;
1241 }
7faafbd6 1242 dodie "Failed on boot" if $failed;
5a391fbf 1243
2b7d9b21 1244 do_run_test or $failed = 1;
5f9b6ced 1245 }
7faafbd6 1246 end_monitor;
5f9b6ced
SR
1247 }
1248
1249 if ($failed) {
0a05c769 1250 $result = 0;
5f9b6ced 1251 } else {
0a05c769
SR
1252 $result = 1;
1253 }
4025bc62
SR
1254
1255 # reboot the box to a kernel we can ssh to
1256 if ($type ne "build") {
1257 bisect_reboot;
1258 }
0a05c769
SR
1259 $in_bisect = 0;
1260
1261 return $result;
1262}
1263
1264sub run_bisect {
1265 my ($type) = @_;
1266 my $buildtype = "oldconfig";
1267
1268 # We should have a minconfig to use?
1269 if (defined($minconfig)) {
1270 $buildtype = "useconfig:$minconfig";
5f9b6ced
SR
1271 }
1272
0a05c769
SR
1273 my $ret = run_bisect_test $type, $buildtype;
1274
c960bb9f
SR
1275 if ($bisect_manual) {
1276 $ret = answer_bisect;
1277 }
0a05c769 1278
d6ce2a0b
SR
1279 # Are we looking for where it worked, not failed?
1280 if ($reverse_bisect) {
0a05c769 1281 $ret = !$ret;
d6ce2a0b
SR
1282 }
1283
c23dca7c 1284 if ($ret > 0) {
0a05c769 1285 return "good";
c23dca7c 1286 } elsif ($ret == 0) {
0a05c769 1287 return "bad";
c23dca7c
SR
1288 } elsif ($bisect_skip) {
1289 doprint "HIT A BAD COMMIT ... SKIPPING\n";
1290 return "skip";
0a05c769 1291 }
5f9b6ced
SR
1292}
1293
1294sub bisect {
1295 my ($i) = @_;
1296
1297 my $result;
1298
1299 die "BISECT_GOOD[$i] not defined\n" if (!defined($opt{"BISECT_GOOD[$i]"}));
1300 die "BISECT_BAD[$i] not defined\n" if (!defined($opt{"BISECT_BAD[$i]"}));
1301 die "BISECT_TYPE[$i] not defined\n" if (!defined($opt{"BISECT_TYPE[$i]"}));
1302
1303 my $good = $opt{"BISECT_GOOD[$i]"};
1304 my $bad = $opt{"BISECT_BAD[$i]"};
1305 my $type = $opt{"BISECT_TYPE[$i]"};
a75fecec
SR
1306 my $start = $opt{"BISECT_START[$i]"};
1307 my $replay = $opt{"BISECT_REPLAY[$i]"};
3410f6fd
SR
1308 my $start_files = $opt{"BISECT_FILES[$i]"};
1309
1310 if (defined($start_files)) {
1311 $start_files = " -- " . $start_files;
1312 } else {
1313 $start_files = "";
1314 }
5f9b6ced 1315
a57419b3
SR
1316 # convert to true sha1's
1317 $good = get_sha1($good);
1318 $bad = get_sha1($bad);
1319
d6ce2a0b
SR
1320 if (defined($opt{"BISECT_REVERSE[$i]"}) &&
1321 $opt{"BISECT_REVERSE[$i]"} == 1) {
1322 doprint "Performing a reverse bisect (bad is good, good is bad!)\n";
1323 $reverse_bisect = 1;
1324 } else {
1325 $reverse_bisect = 0;
1326 }
1327
a75fecec
SR
1328 # Can't have a test without having a test to run
1329 if ($type eq "test" && !defined($run_test)) {
1330 $type = "boot";
1331 }
1332
1333 my $check = $opt{"BISECT_CHECK[$i]"};
1334 if (defined($check) && $check ne "0") {
1335
1336 # get current HEAD
a57419b3 1337 my $head = get_sha1("HEAD");
a75fecec
SR
1338
1339 if ($check ne "good") {
1340 doprint "TESTING BISECT BAD [$bad]\n";
1341 run_command "git checkout $bad" or
1342 die "Failed to checkout $bad";
1343
1344 $result = run_bisect $type;
1345
1346 if ($result ne "bad") {
1347 fail "Tested BISECT_BAD [$bad] and it succeeded" and return 0;
1348 }
1349 }
1350
1351 if ($check ne "bad") {
1352 doprint "TESTING BISECT GOOD [$good]\n";
1353 run_command "git checkout $good" or
1354 die "Failed to checkout $good";
1355
1356 $result = run_bisect $type;
1357
1358 if ($result ne "good") {
1359 fail "Tested BISECT_GOOD [$good] and it failed" and return 0;
1360 }
1361 }
1362
1363 # checkout where we started
1364 run_command "git checkout $head" or
1365 die "Failed to checkout $head";
1366 }
1367
3410f6fd 1368 run_command "git bisect start$start_files" or
a75fecec 1369 dodie "could not start bisect";
5f9b6ced
SR
1370
1371 run_command "git bisect good $good" or
a75fecec 1372 dodie "could not set bisect good to $good";
5f9b6ced 1373
a75fecec
SR
1374 run_git_bisect "git bisect bad $bad" or
1375 dodie "could not set bisect bad to $bad";
5f9b6ced 1376
a75fecec
SR
1377 if (defined($replay)) {
1378 run_command "git bisect replay $replay" or
1379 dodie "failed to run replay";
5a391fbf
SR
1380 }
1381
a75fecec
SR
1382 if (defined($start)) {
1383 run_command "git checkout $start" or
1384 dodie "failed to checkout $start";
1385 }
1386
1387 my $test;
5f9b6ced
SR
1388 do {
1389 $result = run_bisect $type;
a75fecec
SR
1390 $test = run_git_bisect "git bisect $result";
1391 } while ($test);
5f9b6ced
SR
1392
1393 run_command "git bisect log" or
1394 dodie "could not capture git bisect log";
1395
1396 run_command "git bisect reset" or
1397 dodie "could not reset git bisect";
1398
1399 doprint "Bad commit was [$bisect_bad]\n";
1400
0a05c769
SR
1401 success $i;
1402}
1403
1404my %config_ignore;
1405my %config_set;
1406
1407my %config_list;
1408my %null_config;
1409
1410my %dependency;
1411
1412sub process_config_ignore {
1413 my ($config) = @_;
1414
1415 open (IN, $config)
1416 or dodie "Failed to read $config";
1417
1418 while (<IN>) {
1419 if (/^(.*?(CONFIG\S*)(=.*| is not set))/) {
1420 $config_ignore{$2} = $1;
1421 }
1422 }
1423
1424 close(IN);
1425}
1426
1427sub read_current_config {
1428 my ($config_ref) = @_;
1429
1430 %{$config_ref} = ();
1431 undef %{$config_ref};
1432
1433 my @key = keys %{$config_ref};
1434 if ($#key >= 0) {
1435 print "did not delete!\n";
1436 exit;
1437 }
1438 open (IN, "$output_config");
1439
1440 while (<IN>) {
1441 if (/^(CONFIG\S+)=(.*)/) {
1442 ${$config_ref}{$1} = $2;
1443 }
1444 }
1445 close(IN);
1446}
1447
1448sub get_dependencies {
1449 my ($config) = @_;
1450
1451 my $arr = $dependency{$config};
1452 if (!defined($arr)) {
1453 return ();
1454 }
1455
1456 my @deps = @{$arr};
1457
1458 foreach my $dep (@{$arr}) {
1459 print "ADD DEP $dep\n";
1460 @deps = (@deps, get_dependencies $dep);
1461 }
1462
1463 return @deps;
1464}
1465
1466sub create_config {
1467 my @configs = @_;
1468
1469 open(OUT, ">$output_config") or dodie "Can not write to $output_config";
1470
1471 foreach my $config (@configs) {
1472 print OUT "$config_set{$config}\n";
1473 my @deps = get_dependencies $config;
1474 foreach my $dep (@deps) {
1475 print OUT "$config_set{$dep}\n";
1476 }
1477 }
1478
1479 foreach my $config (keys %config_ignore) {
1480 print OUT "$config_ignore{$config}\n";
1481 }
1482 close(OUT);
1483
1484# exit;
612b9e9b 1485 make_oldconfig "";
0a05c769
SR
1486}
1487
1488sub compare_configs {
1489 my (%a, %b) = @_;
1490
1491 foreach my $item (keys %a) {
1492 if (!defined($b{$item})) {
1493 print "diff $item\n";
1494 return 1;
1495 }
1496 delete $b{$item};
1497 }
1498
1499 my @keys = keys %b;
1500 if ($#keys) {
1501 print "diff2 $keys[0]\n";
1502 }
1503 return -1 if ($#keys >= 0);
1504
1505 return 0;
1506}
1507
1508sub run_config_bisect_test {
1509 my ($type) = @_;
1510
1511 return run_bisect_test $type, "oldconfig";
1512}
1513
1514sub process_passed {
1515 my (%configs) = @_;
1516
1517 doprint "These configs had no failure: (Enabling them for further compiles)\n";
1518 # Passed! All these configs are part of a good compile.
1519 # Add them to the min options.
1520 foreach my $config (keys %configs) {
1521 if (defined($config_list{$config})) {
1522 doprint " removing $config\n";
1523 $config_ignore{$config} = $config_list{$config};
1524 delete $config_list{$config};
1525 }
1526 }
f1a27850
SR
1527 doprint "config copied to $outputdir/config_good\n";
1528 run_command "cp -f $output_config $outputdir/config_good";
0a05c769
SR
1529}
1530
1531sub process_failed {
1532 my ($config) = @_;
1533
1534 doprint "\n\n***************************************\n";
1535 doprint "Found bad config: $config\n";
1536 doprint "***************************************\n\n";
1537}
1538
1539sub run_config_bisect {
1540
1541 my @start_list = keys %config_list;
1542
1543 if ($#start_list < 0) {
1544 doprint "No more configs to test!!!\n";
1545 return -1;
1546 }
1547
1548 doprint "***** RUN TEST ***\n";
1549 my $type = $opt{"CONFIG_BISECT_TYPE[$iteration]"};
1550 my $ret;
1551 my %current_config;
1552
1553 my $count = $#start_list + 1;
1554 doprint " $count configs to test\n";
1555
1556 my $half = int($#start_list / 2);
1557
1558 do {
1559 my @tophalf = @start_list[0 .. $half];
1560
1561 create_config @tophalf;
1562 read_current_config \%current_config;
1563
1564 $count = $#tophalf + 1;
1565 doprint "Testing $count configs\n";
1566 my $found = 0;
1567 # make sure we test something
1568 foreach my $config (@tophalf) {
1569 if (defined($current_config{$config})) {
1570 logit " $config\n";
1571 $found = 1;
1572 }
1573 }
1574 if (!$found) {
1575 # try the other half
1576 doprint "Top half produced no set configs, trying bottom half\n";
1577 @tophalf = @start_list[$half .. $#start_list];
1578 create_config @tophalf;
1579 read_current_config \%current_config;
1580 foreach my $config (@tophalf) {
1581 if (defined($current_config{$config})) {
1582 logit " $config\n";
1583 $found = 1;
1584 }
1585 }
1586 if (!$found) {
1587 doprint "Failed: Can't make new config with current configs\n";
1588 foreach my $config (@start_list) {
1589 doprint " CONFIG: $config\n";
1590 }
1591 return -1;
1592 }
1593 $count = $#tophalf + 1;
1594 doprint "Testing $count configs\n";
1595 }
1596
1597 $ret = run_config_bisect_test $type;
c960bb9f
SR
1598 if ($bisect_manual) {
1599 $ret = answer_bisect;
1600 }
0a05c769
SR
1601 if ($ret) {
1602 process_passed %current_config;
1603 return 0;
1604 }
1605
1606 doprint "This config had a failure.\n";
1607 doprint "Removing these configs that were not set in this config:\n";
f1a27850
SR
1608 doprint "config copied to $outputdir/config_bad\n";
1609 run_command "cp -f $output_config $outputdir/config_bad";
0a05c769
SR
1610
1611 # A config exists in this group that was bad.
1612 foreach my $config (keys %config_list) {
1613 if (!defined($current_config{$config})) {
1614 doprint " removing $config\n";
1615 delete $config_list{$config};
1616 }
1617 }
1618
1619 @start_list = @tophalf;
1620
1621 if ($#start_list == 0) {
1622 process_failed $start_list[0];
1623 return 1;
1624 }
1625
1626 # remove half the configs we are looking at and see if
1627 # they are good.
1628 $half = int($#start_list / 2);
1629 } while ($half > 0);
1630
c960bb9f
SR
1631 # we found a single config, try it again unless we are running manually
1632
1633 if ($bisect_manual) {
1634 process_failed $start_list[0];
1635 return 1;
1636 }
1637
0a05c769
SR
1638 my @tophalf = @start_list[0 .. 0];
1639
1640 $ret = run_config_bisect_test $type;
1641 if ($ret) {
1642 process_passed %current_config;
1643 return 0;
1644 }
1645
1646 process_failed $start_list[0];
1647 return 1;
1648}
1649
1650sub config_bisect {
1651 my ($i) = @_;
1652
1653 my $start_config = $opt{"CONFIG_BISECT[$i]"};
1654
1655 my $tmpconfig = "$tmpdir/use_config";
1656
1657 # Make the file with the bad config and the min config
1658 if (defined($minconfig)) {
1659 # read the min config for things to ignore
1660 run_command "cp $minconfig $tmpconfig" or
1661 dodie "failed to copy $minconfig to $tmpconfig";
1662 } else {
1663 unlink $tmpconfig;
1664 }
1665
1666 # Add other configs
1667 if (defined($addconfig)) {
1668 run_command "cat $addconfig >> $tmpconfig" or
1669 dodie "failed to append $addconfig";
1670 }
1671
1672 my $defconfig = "";
1673 if (-f $tmpconfig) {
1674 $defconfig = "KCONFIG_ALLCONFIG=$tmpconfig";
1675 process_config_ignore $tmpconfig;
1676 }
1677
1678 # now process the start config
1679 run_command "cp $start_config $output_config" or
1680 dodie "failed to copy $start_config to $output_config";
1681
1682 # read directly what we want to check
1683 my %config_check;
1684 open (IN, $output_config)
1685 or dodie "faied to open $output_config";
1686
1687 while (<IN>) {
1688 if (/^((CONFIG\S*)=.*)/) {
1689 $config_check{$2} = $1;
1690 }
1691 }
1692 close(IN);
1693
1694 # Now run oldconfig with the minconfig (and addconfigs)
612b9e9b 1695 make_oldconfig $defconfig;
0a05c769
SR
1696
1697 # check to see what we lost (or gained)
1698 open (IN, $output_config)
1699 or dodie "Failed to read $start_config";
1700
1701 my %removed_configs;
1702 my %added_configs;
1703
1704 while (<IN>) {
1705 if (/^((CONFIG\S*)=.*)/) {
1706 # save off all options
1707 $config_set{$2} = $1;
1708 if (defined($config_check{$2})) {
1709 if (defined($config_ignore{$2})) {
1710 $removed_configs{$2} = $1;
1711 } else {
1712 $config_list{$2} = $1;
1713 }
1714 } elsif (!defined($config_ignore{$2})) {
1715 $added_configs{$2} = $1;
1716 $config_list{$2} = $1;
1717 }
1718 }
1719 }
1720 close(IN);
1721
1722 my @confs = keys %removed_configs;
1723 if ($#confs >= 0) {
1724 doprint "Configs overridden by default configs and removed from check:\n";
1725 foreach my $config (@confs) {
1726 doprint " $config\n";
1727 }
1728 }
1729 @confs = keys %added_configs;
1730 if ($#confs >= 0) {
1731 doprint "Configs appearing in make oldconfig and added:\n";
1732 foreach my $config (@confs) {
1733 doprint " $config\n";
1734 }
1735 }
1736
1737 my %config_test;
1738 my $once = 0;
1739
1740 # Sometimes kconfig does weird things. We must make sure
1741 # that the config we autocreate has everything we need
1742 # to test, otherwise we may miss testing configs, or
1743 # may not be able to create a new config.
1744 # Here we create a config with everything set.
1745 create_config (keys %config_list);
1746 read_current_config \%config_test;
1747 foreach my $config (keys %config_list) {
1748 if (!defined($config_test{$config})) {
1749 if (!$once) {
1750 $once = 1;
1751 doprint "Configs not produced by kconfig (will not be checked):\n";
1752 }
1753 doprint " $config\n";
1754 delete $config_list{$config};
1755 }
1756 }
1757 my $ret;
1758 do {
1759 $ret = run_config_bisect;
1760 } while (!$ret);
1761
1762 return $ret if ($ret < 0);
5f9b6ced
SR
1763
1764 success $i;
1765}
1766
6c5ee0be
SR
1767sub patchcheck {
1768 my ($i) = @_;
1769
1770 die "PATCHCHECK_START[$i] not defined\n"
1771 if (!defined($opt{"PATCHCHECK_START[$i]"}));
1772 die "PATCHCHECK_TYPE[$i] not defined\n"
1773 if (!defined($opt{"PATCHCHECK_TYPE[$i]"}));
1774
1775 my $start = $opt{"PATCHCHECK_START[$i]"};
1776
1777 my $end = "HEAD";
1778 if (defined($opt{"PATCHCHECK_END[$i]"})) {
1779 $end = $opt{"PATCHCHECK_END[$i]"};
1780 }
1781
a57419b3
SR
1782 # Get the true sha1's since we can use things like HEAD~3
1783 $start = get_sha1($start);
1784 $end = get_sha1($end);
1785
6c5ee0be
SR
1786 my $type = $opt{"PATCHCHECK_TYPE[$i]"};
1787
1788 # Can't have a test without having a test to run
1789 if ($type eq "test" && !defined($run_test)) {
1790 $type = "boot";
1791 }
1792
1793 open (IN, "git log --pretty=oneline $end|") or
1794 dodie "could not get git list";
1795
1796 my @list;
1797
1798 while (<IN>) {
1799 chomp;
1800 $list[$#list+1] = $_;
1801 last if (/^$start/);
1802 }
1803 close(IN);
1804
1805 if ($list[$#list] !~ /^$start/) {
2b7d9b21 1806 fail "SHA1 $start not found";
6c5ee0be
SR
1807 }
1808
1809 # go backwards in the list
1810 @list = reverse @list;
1811
1812 my $save_clean = $noclean;
1813
1814 $in_patchcheck = 1;
1815 foreach my $item (@list) {
1816 my $sha1 = $item;
1817 $sha1 =~ s/^([[:xdigit:]]+).*/$1/;
1818
1819 doprint "\nProcessing commit $item\n\n";
1820
1821 run_command "git checkout $sha1" or
1822 die "Failed to checkout $sha1";
1823
1824 # only clean on the first and last patch
1825 if ($item eq $list[0] ||
1826 $item eq $list[$#list]) {
1827 $noclean = $save_clean;
1828 } else {
1829 $noclean = 1;
1830 }
1831
1832 if (defined($minconfig)) {
2b7d9b21 1833 build "useconfig:$minconfig" or return 0;
6c5ee0be
SR
1834 } else {
1835 # ?? no config to use?
2b7d9b21 1836 build "oldconfig" or return 0;
6c5ee0be
SR
1837 }
1838
2b7d9b21 1839 check_buildlog $sha1 or return 0;
6c5ee0be
SR
1840
1841 next if ($type eq "build");
1842
1843 get_grub_index;
1844 get_version;
1845 install;
6c5ee0be 1846
7faafbd6
SR
1847 my $failed = 0;
1848
1849 start_monitor;
1850 monitor or $failed = 1;
1851
1852 if (!$failed && $type ne "boot"){
1853 do_run_test or $failed = 1;
1854 }
1855 end_monitor;
1856 return 0 if ($failed);
1857
6c5ee0be
SR
1858 }
1859 $in_patchcheck = 0;
1860 success $i;
2b7d9b21
SR
1861
1862 return 1;
6c5ee0be
SR
1863}
1864
8d1491ba
SR
1865$#ARGV < 1 or die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n";
1866
1867if ($#ARGV == 0) {
1868 $ktest_config = $ARGV[0];
1869 if (! -f $ktest_config) {
1870 print "$ktest_config does not exist.\n";
1871 my $ans;
1872 for (;;) {
1873 print "Create it? [Y/n] ";
1874 $ans = <STDIN>;
1875 chomp $ans;
1876 if ($ans =~ /^\s*$/) {
1877 $ans = "y";
1878 }
1879 last if ($ans =~ /^y$/i || $ans =~ /^n$/i);
1880 print "Please answer either 'y' or 'n'.\n";
1881 }
1882 if ($ans !~ /^y$/i) {
1883 exit 0;
1884 }
1885 }
1886} else {
1887 $ktest_config = "ktest.conf";
1888}
1889
1890if (! -f $ktest_config) {
1891 open(OUT, ">$ktest_config") or die "Can not create $ktest_config";
1892 print OUT << "EOF"
1893# Generated by ktest.pl
1894#
1895# Define each test with TEST_START
1896# The config options below it will override the defaults
1897TEST_START
1898
1899DEFAULTS
1900EOF
1901;
1902 close(OUT);
1903}
1904read_config $ktest_config;
1905
1906# Append any configs entered in manually to the config file.
1907my @new_configs = keys %entered_configs;
1908if ($#new_configs >= 0) {
1909 print "\nAppending entered in configs to $ktest_config\n";
1910 open(OUT, ">>$ktest_config") or die "Can not append to $ktest_config";
1911 foreach my $config (@new_configs) {
1912 print OUT "$config = $entered_configs{$config}\n";
1913 $opt{$config} = $entered_configs{$config};
1914 }
1915}
2545eb61 1916
2b7d9b21
SR
1917if ($opt{"CLEAR_LOG"} && defined($opt{"LOG_FILE"})) {
1918 unlink $opt{"LOG_FILE"};
1919}
2545eb61 1920
2b7d9b21
SR
1921doprint "\n\nSTARTING AUTOMATED TESTS\n\n";
1922
a57419b3
SR
1923for (my $i = 0, my $repeat = 1; $i <= $opt{"NUM_TESTS"}; $i += $repeat) {
1924
1925 if (!$i) {
1926 doprint "DEFAULT OPTIONS:\n";
1927 } else {
1928 doprint "\nTEST $i OPTIONS";
1929 if (defined($repeat_tests{$i})) {
1930 $repeat = $repeat_tests{$i};
1931 doprint " ITERATE $repeat";
1932 }
1933 doprint "\n";
1934 }
1935
1936 foreach my $option (sort keys %opt) {
1937
1938 if ($option =~ /\[(\d+)\]$/) {
1939 next if ($i != $1);
1940 } else {
1941 next if ($i);
1942 }
1943
1944 doprint "$option = $opt{$option}\n";
1945 }
2b7d9b21 1946}
2545eb61 1947
a75fecec 1948sub set_test_option {
5a391fbf 1949 my ($name, $i) = @_;
2545eb61 1950
5a391fbf 1951 my $option = "$name\[$i\]";
5c42fc5b 1952
5a391fbf
SR
1953 if (defined($opt{$option})) {
1954 return $opt{$option};
5f9b6ced
SR
1955 }
1956
a57419b3
SR
1957 foreach my $test (keys %repeat_tests) {
1958 if ($i >= $test &&
1959 $i < $test + $repeat_tests{$test}) {
1960 $option = "$name\[$test\]";
1961 if (defined($opt{$option})) {
1962 return $opt{$option};
1963 }
1964 }
1965 }
1966
5a391fbf
SR
1967 if (defined($opt{$name})) {
1968 return $opt{$name};
2545eb61
SR
1969 }
1970
5a391fbf
SR
1971 return undef;
1972}
1973
1974# First we need to do is the builds
a75fecec
SR
1975for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) {
1976
576f627c
SR
1977 $iteration = $i;
1978
a75fecec
SR
1979 my $makecmd = set_test_option("MAKE_CMD", $i);
1980
1981 $machine = set_test_option("MACHINE", $i);
e48c5293 1982 $ssh_user = set_test_option("SSH_USER", $i);
a75fecec
SR
1983 $tmpdir = set_test_option("TMP_DIR", $i);
1984 $outputdir = set_test_option("OUTPUT_DIR", $i);
1985 $builddir = set_test_option("BUILD_DIR", $i);
1986 $test_type = set_test_option("TEST_TYPE", $i);
1987 $build_type = set_test_option("BUILD_TYPE", $i);
1988 $build_options = set_test_option("BUILD_OPTIONS", $i);
1989 $power_cycle = set_test_option("POWER_CYCLE", $i);
e48c5293 1990 $reboot = set_test_option("REBOOT", $i);
a75fecec
SR
1991 $noclean = set_test_option("BUILD_NOCLEAN", $i);
1992 $minconfig = set_test_option("MIN_CONFIG", $i);
1993 $run_test = set_test_option("TEST", $i);
1994 $addconfig = set_test_option("ADD_CONFIG", $i);
1995 $reboot_type = set_test_option("REBOOT_TYPE", $i);
1996 $grub_menu = set_test_option("GRUB_MENU", $i);
8b37ca8c 1997 $post_install = set_test_option("POST_INSTALL", $i);
a75fecec
SR
1998 $reboot_script = set_test_option("REBOOT_SCRIPT", $i);
1999 $reboot_on_error = set_test_option("REBOOT_ON_ERROR", $i);
2000 $poweroff_on_error = set_test_option("POWEROFF_ON_ERROR", $i);
2001 $die_on_failure = set_test_option("DIE_ON_FAILURE", $i);
2002 $power_off = set_test_option("POWER_OFF", $i);
576f627c
SR
2003 $powercycle_after_reboot = set_test_option("POWERCYCLE_AFTER_REBOOT", $i);
2004 $poweroff_after_halt = set_test_option("POWEROFF_AFTER_HALT", $i);
a75fecec
SR
2005 $sleep_time = set_test_option("SLEEP_TIME", $i);
2006 $bisect_sleep_time = set_test_option("BISECT_SLEEP_TIME", $i);
c960bb9f 2007 $bisect_manual = set_test_option("BISECT_MANUAL", $i);
c23dca7c 2008 $bisect_skip = set_test_option("BISECT_SKIP", $i);
a75fecec
SR
2009 $store_failures = set_test_option("STORE_FAILURES", $i);
2010 $timeout = set_test_option("TIMEOUT", $i);
2011 $booted_timeout = set_test_option("BOOTED_TIMEOUT", $i);
2012 $console = set_test_option("CONSOLE", $i);
2013 $success_line = set_test_option("SUCCESS_LINE", $i);
1c8a617a
SR
2014 $stop_after_success = set_test_option("STOP_AFTER_SUCCESS", $i);
2015 $stop_after_failure = set_test_option("STOP_AFTER_FAILURE", $i);
2d01b26a 2016 $stop_test_after = set_test_option("STOP_TEST_AFTER", $i);
a75fecec 2017 $build_target = set_test_option("BUILD_TARGET", $i);
e48c5293
SR
2018 $ssh_exec = set_test_option("SSH_EXEC", $i);
2019 $scp_to_target = set_test_option("SCP_TO_TARGET", $i);
a75fecec
SR
2020 $target_image = set_test_option("TARGET_IMAGE", $i);
2021 $localversion = set_test_option("LOCALVERSION", $i);
2022
2023 chdir $builddir || die "can't change directory to $builddir";
2024
2025 if (!-d $tmpdir) {
2026 mkpath($tmpdir) or
2027 die "can't create $tmpdir";
2028 }
1a5cfce3 2029
e48c5293
SR
2030 $ENV{"SSH_USER"} = $ssh_user;
2031 $ENV{"MACHINE"} = $machine;
2032
a75fecec
SR
2033 $target = "$ssh_user\@$machine";
2034
2035 $buildlog = "$tmpdir/buildlog-$machine";
2036 $dmesg = "$tmpdir/dmesg-$machine";
2037 $make = "$makecmd O=$outputdir";
51ad1dd1 2038 $output_config = "$outputdir/.config";
a75fecec
SR
2039
2040 if ($reboot_type eq "grub") {
576f627c 2041 dodie "GRUB_MENU not defined" if (!defined($grub_menu));
a75fecec 2042 } elsif (!defined($reboot_script)) {
576f627c 2043 dodie "REBOOT_SCRIPT not defined"
a75fecec
SR
2044 }
2045
2046 my $run_type = $build_type;
2047 if ($test_type eq "patchcheck") {
2048 $run_type = $opt{"PATCHCHECK_TYPE[$i]"};
2049 } elsif ($test_type eq "bisect") {
2050 $run_type = $opt{"BISECT_TYPE[$i]"};
0a05c769
SR
2051 } elsif ($test_type eq "config_bisect") {
2052 $run_type = $opt{"CONFIG_BISECT_TYPE[$i]"};
a75fecec
SR
2053 }
2054
2055 # mistake in config file?
2056 if (!defined($run_type)) {
2057 $run_type = "ERROR";
2058 }
5a391fbf 2059
2545eb61 2060 doprint "\n\n";
a75fecec 2061 doprint "RUNNING TEST $i of $opt{NUM_TESTS} with option $test_type $run_type\n\n";
7faafbd6
SR
2062
2063 unlink $dmesg;
2064 unlink $buildlog;
2545eb61 2065
2b7d9b21
SR
2066 if (!defined($minconfig)) {
2067 $minconfig = $addconfig;
2068
2069 } elsif (defined($addconfig)) {
9be2e6b5 2070 run_command "cat $addconfig $minconfig > $tmpdir/add_config" or
2b7d9b21 2071 dodie "Failed to create temp config";
9be2e6b5 2072 $minconfig = "$tmpdir/add_config";
2b7d9b21
SR
2073 }
2074
6c5ee0be
SR
2075 my $checkout = $opt{"CHECKOUT[$i]"};
2076 if (defined($checkout)) {
2077 run_command "git checkout $checkout" or
2078 die "failed to checkout $checkout";
2079 }
2080
a75fecec 2081 if ($test_type eq "bisect") {
5f9b6ced
SR
2082 bisect $i;
2083 next;
0a05c769
SR
2084 } elsif ($test_type eq "config_bisect") {
2085 config_bisect $i;
2086 next;
a75fecec 2087 } elsif ($test_type eq "patchcheck") {
6c5ee0be
SR
2088 patchcheck $i;
2089 next;
2545eb61 2090 }
2545eb61 2091
7faafbd6
SR
2092 if ($build_type ne "nobuild") {
2093 build $build_type or next;
2545eb61
SR
2094 }
2095
a75fecec
SR
2096 if ($test_type ne "build") {
2097 get_grub_index;
2098 get_version;
2099 install;
5a391fbf 2100
a75fecec
SR
2101 my $failed = 0;
2102 start_monitor;
2103 monitor or $failed = 1;;
2104
2105 if (!$failed && $test_type ne "boot" && defined($run_test)) {
2106 do_run_test or $failed = 1;
2107 }
2108 end_monitor;
2109 next if ($failed);
5a391fbf
SR
2110 }
2111
5f9b6ced 2112 success $i;
2545eb61
SR
2113}
2114
5c42fc5b 2115if ($opt{"POWEROFF_ON_SUCCESS"}) {
75c3fda7 2116 halt;
576f627c 2117} elsif ($opt{"REBOOT_ON_SUCCESS"} && !do_not_reboot) {
75c3fda7 2118 reboot;
5c42fc5b 2119}
75c3fda7 2120
e48c5293
SR
2121doprint "\n $successes of $opt{NUM_TESTS} tests were successful\n\n";
2122
2545eb61 2123exit 0;
This page took 0.150119 seconds and 5 git commands to generate.