Commit | Line | Data |
---|---|---|
2545eb61 | 1 | #!/usr/bin/perl -w |
d6ce2a0b SR |
2 | # |
3 | # Copywrite 2010 - Steven Rostedt <srostedt@redhat.com>, Red Hat Inc. | |
4 | # Licensed under the terms of the GNU GPL License version 2 | |
5 | # | |
2545eb61 SR |
6 | |
7 | use strict; | |
8 | use IPC::Open2; | |
9 | use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK); | |
10 | use FileHandle; | |
11 | ||
12 | $#ARGV >= 0 || die "usage: autotest.pl config-file\n"; | |
13 | ||
14 | $| = 1; | |
15 | ||
16 | my %opt; | |
17 | ||
18 | #default opts | |
19 | $opt{"NUM_BUILDS"} = 5; | |
20 | $opt{"DEFAULT_BUILD_TYPE"} = "randconfig"; | |
21 | $opt{"MAKE_CMD"} = "make"; | |
22 | $opt{"TIMEOUT"} = 50; | |
23 | $opt{"TMP_DIR"} = "/tmp/autotest"; | |
24 | $opt{"SLEEP_TIME"} = 60; # sleep time between tests | |
5c42fc5b | 25 | $opt{"BUILD_NOCLEAN"} = 0; |
75c3fda7 | 26 | $opt{"REBOOT_ON_ERROR"} = 0; |
5c42fc5b | 27 | $opt{"POWEROFF_ON_ERROR"} = 0; |
1a5cfce3 | 28 | $opt{"REBOOT_ON_SUCCESS"} = 1; |
5c42fc5b | 29 | $opt{"POWEROFF_ON_SUCCESS"} = 0; |
75c3fda7 | 30 | $opt{"BUILD_OPTIONS"} = ""; |
5a391fbf | 31 | $opt{"BISECT_SLEEP_TIME"} = 10; # sleep time between bisects |
2545eb61 SR |
32 | |
33 | my $version; | |
2545eb61 SR |
34 | my $grub_number; |
35 | my $target; | |
36 | my $make; | |
5c42fc5b | 37 | my $noclean; |
5f9b6ced SR |
38 | my $minconfig; |
39 | my $in_bisect = 0; | |
40 | my $bisect_bad = ""; | |
d6ce2a0b | 41 | my $reverse_bisect; |
6c5ee0be | 42 | my $in_patchcheck = 0; |
5a391fbf | 43 | my $run_test; |
6c5ee0be | 44 | my $redirect; |
2545eb61 SR |
45 | |
46 | sub read_config { | |
47 | my ($config) = @_; | |
48 | ||
49 | open(IN, $config) || die "can't read file $config"; | |
50 | ||
51 | while (<IN>) { | |
52 | ||
53 | # ignore blank lines and comments | |
54 | next if (/^\s*$/ || /\s*\#/); | |
55 | ||
56 | if (/^\s*(\S+)\s*=\s*(.*?)\s*$/) { | |
57 | my $lvalue = $1; | |
58 | my $rvalue = $2; | |
59 | ||
60 | $opt{$lvalue} = $rvalue; | |
61 | } | |
62 | } | |
63 | ||
64 | close(IN); | |
65 | } | |
66 | ||
5f9b6ced | 67 | sub logit { |
2545eb61 SR |
68 | if (defined($opt{"LOG_FILE"})) { |
69 | open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}"; | |
70 | print OUT @_; | |
71 | close(OUT); | |
72 | } | |
73 | } | |
74 | ||
5f9b6ced SR |
75 | sub doprint { |
76 | print @_; | |
77 | logit @_; | |
78 | } | |
79 | ||
5c42fc5b | 80 | sub dodie { |
5a391fbf | 81 | doprint "CRITICAL FAILURE... ", @_, "\n"; |
5c42fc5b | 82 | |
75c3fda7 SR |
83 | if ($opt{"REBOOT_ON_ERROR"}) { |
84 | doprint "REBOOTING\n"; | |
85 | `$opt{"POWER_CYCLE"}`; | |
86 | ||
87 | } elsif ($opt{"POWEROFF_ON_ERROR"} && defined($opt{"POWER_OFF"})) { | |
5c42fc5b SR |
88 | doprint "POWERING OFF\n"; |
89 | `$opt{"POWER_OFF"}`; | |
90 | } | |
75c3fda7 | 91 | |
5c42fc5b SR |
92 | die @_; |
93 | } | |
94 | ||
2545eb61 SR |
95 | sub run_command { |
96 | my ($command) = @_; | |
d6ce2a0b SR |
97 | my $dolog = 0; |
98 | my $dord = 0; | |
99 | my $pid; | |
100 | ||
101 | doprint("$command ... "); | |
102 | ||
103 | $pid = open(CMD, "$command 2>&1 |") or | |
104 | dodie "unable to exec $command"; | |
2545eb61 SR |
105 | |
106 | if (defined($opt{"LOG_FILE"})) { | |
d6ce2a0b SR |
107 | open(LOG, ">>$opt{LOG_FILE}") or |
108 | dodie "failed to write to log"; | |
109 | $dolog = 1; | |
6c5ee0be SR |
110 | } |
111 | ||
112 | if (defined($redirect)) { | |
d6ce2a0b SR |
113 | open (RD, ">$redirect") or |
114 | dodie "failed to write to redirect $redirect"; | |
115 | $dord = 1; | |
2545eb61 SR |
116 | } |
117 | ||
d6ce2a0b SR |
118 | while (<CMD>) { |
119 | print LOG if ($dolog); | |
120 | print RD if ($dord); | |
121 | } | |
2545eb61 | 122 | |
d6ce2a0b | 123 | waitpid($pid, 0); |
2545eb61 SR |
124 | my $failed = $?; |
125 | ||
d6ce2a0b SR |
126 | close(CMD); |
127 | close(LOG) if ($dolog); | |
128 | close(RD) if ($dord); | |
129 | ||
2545eb61 SR |
130 | if ($failed) { |
131 | doprint "FAILED!\n"; | |
132 | } else { | |
133 | doprint "SUCCESS\n"; | |
134 | } | |
135 | ||
5f9b6ced SR |
136 | return !$failed; |
137 | } | |
138 | ||
139 | sub get_grub_index { | |
140 | ||
5a391fbf | 141 | return if (defined($grub_number)); |
5f9b6ced SR |
142 | |
143 | doprint "Find grub menu ... "; | |
144 | $grub_number = -1; | |
145 | open(IN, "ssh $target cat /boot/grub/menu.lst |") | |
146 | or die "unable to get menu.lst"; | |
147 | while (<IN>) { | |
148 | if (/^\s*title\s+$opt{GRUB_MENU}\s*$/) { | |
149 | $grub_number++; | |
150 | last; | |
151 | } elsif (/^\s*title\s/) { | |
152 | $grub_number++; | |
153 | } | |
154 | } | |
155 | close(IN); | |
156 | ||
157 | die "Could not find '$opt{GRUB_MENU}' in /boot/grub/menu on $opt{MACHINE}" | |
158 | if ($grub_number < 0); | |
159 | doprint "$grub_number\n"; | |
2545eb61 SR |
160 | } |
161 | ||
162 | my $timeout = $opt{"TIMEOUT"}; | |
163 | ||
164 | sub wait_for_input | |
165 | { | |
166 | my ($fp, $time) = @_; | |
167 | my $rin; | |
168 | my $ready; | |
169 | my $line; | |
170 | my $ch; | |
171 | ||
172 | if (!defined($time)) { | |
173 | $time = $timeout; | |
174 | } | |
175 | ||
176 | $rin = ''; | |
177 | vec($rin, fileno($fp), 1) = 1; | |
178 | $ready = select($rin, undef, undef, $time); | |
179 | ||
180 | $line = ""; | |
181 | ||
182 | # try to read one char at a time | |
183 | while (sysread $fp, $ch, 1) { | |
184 | $line .= $ch; | |
185 | last if ($ch eq "\n"); | |
186 | } | |
187 | ||
188 | if (!length($line)) { | |
189 | return undef; | |
190 | } | |
191 | ||
192 | return $line; | |
193 | } | |
194 | ||
75c3fda7 | 195 | sub reboot_to { |
2545eb61 SR |
196 | run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; |
197 | } | |
198 | ||
5a391fbf SR |
199 | sub open_console { |
200 | my ($fp) = @_; | |
201 | ||
2545eb61 | 202 | my $flags; |
5a391fbf SR |
203 | |
204 | my $pid = open($fp, "$opt{CONSOLE}|") or | |
205 | dodie "Can't open console $opt{CONSOLE}"; | |
206 | ||
207 | $flags = fcntl($fp, F_GETFL, 0) or | |
208 | dodie "Can't get flags for the socket: $!\n"; | |
209 | $flags = fcntl($fp, F_SETFL, $flags | O_NONBLOCK) or | |
210 | dodie "Can't set flags for the socket: $!\n"; | |
211 | ||
212 | return $pid; | |
213 | } | |
214 | ||
215 | sub close_console { | |
216 | my ($fp, $pid) = @_; | |
217 | ||
218 | doprint "kill child process $pid\n"; | |
219 | kill 2, $pid; | |
220 | ||
221 | print "closing!\n"; | |
222 | close($fp); | |
223 | } | |
224 | ||
225 | sub monitor { | |
2545eb61 SR |
226 | my $booted = 0; |
227 | my $bug = 0; | |
228 | my $pid; | |
5c42fc5b | 229 | my $skip_call_trace = 0; |
5a391fbf | 230 | my $fp = \*IN; |
2545eb61 | 231 | |
5a391fbf | 232 | $pid = open_console($fp); |
2545eb61 SR |
233 | |
234 | my $line; | |
235 | my $full_line = ""; | |
236 | ||
237 | doprint "Wait for monitor to settle down.\n"; | |
238 | # read the monitor and wait for the system to calm down | |
239 | do { | |
5a391fbf | 240 | $line = wait_for_input($fp, 5); |
2545eb61 SR |
241 | } while (defined($line)); |
242 | ||
75c3fda7 | 243 | reboot_to; |
2545eb61 SR |
244 | |
245 | for (;;) { | |
246 | ||
5a391fbf | 247 | $line = wait_for_input($fp); |
2545eb61 SR |
248 | |
249 | last if (!defined($line)); | |
250 | ||
251 | doprint $line; | |
252 | ||
253 | # we are not guaranteed to get a full line | |
254 | $full_line .= $line; | |
255 | ||
256 | if ($full_line =~ /login:/) { | |
257 | $booted = 1; | |
258 | } | |
259 | ||
5c42fc5b SR |
260 | if ($full_line =~ /\[ backtrace testing \]/) { |
261 | $skip_call_trace = 1; | |
262 | } | |
263 | ||
2545eb61 | 264 | if ($full_line =~ /call trace:/i) { |
5c42fc5b SR |
265 | $bug = 1 if (!$skip_call_trace); |
266 | } | |
267 | ||
268 | if ($full_line =~ /\[ end of backtrace testing \]/) { | |
269 | $skip_call_trace = 0; | |
270 | } | |
271 | ||
272 | if ($full_line =~ /Kernel panic -/) { | |
2545eb61 SR |
273 | $bug = 1; |
274 | } | |
275 | ||
276 | if ($line =~ /\n/) { | |
277 | $full_line = ""; | |
278 | } | |
279 | } | |
280 | ||
5a391fbf | 281 | close_console($fp, $pid); |
2545eb61 SR |
282 | |
283 | if (!$booted) { | |
5a391fbf | 284 | return 1 if ($in_bisect); |
5c42fc5b | 285 | dodie "failed - never got a boot prompt.\n"; |
2545eb61 SR |
286 | } |
287 | ||
288 | if ($bug) { | |
5a391fbf | 289 | return 1 if ($in_bisect); |
5c42fc5b | 290 | dodie "failed - got a bug report\n"; |
2545eb61 | 291 | } |
5f9b6ced SR |
292 | |
293 | return 0; | |
2545eb61 SR |
294 | } |
295 | ||
296 | sub install { | |
297 | ||
5f9b6ced | 298 | run_command "scp $opt{OUTPUT_DIR}/$opt{BUILD_TARGET} $target:$opt{TARGET_IMAGE}" or |
5c42fc5b | 299 | dodie "failed to copy image"; |
2545eb61 | 300 | |
5f9b6ced | 301 | my $install_mods = 0; |
2545eb61 | 302 | |
5f9b6ced SR |
303 | # should we process modules? |
304 | $install_mods = 0; | |
305 | open(IN, "$opt{OUTPUT_DIR}/.config") or dodie("Can't read config file"); | |
306 | while (<IN>) { | |
307 | if (/CONFIG_MODULES(=y)?/) { | |
308 | $install_mods = 1 if (defined($1)); | |
309 | last; | |
5c42fc5b | 310 | } |
5f9b6ced SR |
311 | } |
312 | close(IN); | |
5c42fc5b | 313 | |
5f9b6ced SR |
314 | if (!$install_mods) { |
315 | doprint "No modules needed\n"; | |
316 | return; | |
317 | } | |
2545eb61 | 318 | |
5f9b6ced SR |
319 | run_command "$make INSTALL_MOD_PATH=$opt{TMP_DIR} modules_install" or |
320 | dodie "Failed to install modules"; | |
5c42fc5b | 321 | |
5f9b6ced SR |
322 | my $modlib = "/lib/modules/$version"; |
323 | my $modtar = "autotest-mods.tar.bz2"; | |
5c42fc5b | 324 | |
5f9b6ced SR |
325 | run_command "ssh $target rm -rf $modlib" or |
326 | dodie "failed to remove old mods: $modlib"; | |
5c42fc5b | 327 | |
5f9b6ced SR |
328 | # would be nice if scp -r did not follow symbolic links |
329 | run_command "cd $opt{TMP_DIR} && tar -cjf $modtar lib/modules/$version" or | |
330 | dodie "making tarball"; | |
331 | ||
332 | run_command "scp $opt{TMP_DIR}/$modtar $target:/tmp" or | |
333 | dodie "failed to copy modules"; | |
334 | ||
335 | unlink "$opt{TMP_DIR}/$modtar"; | |
336 | ||
337 | run_command "ssh $target '(cd / && tar xf /tmp/$modtar)'" or | |
338 | dodie "failed to tar modules"; | |
2545eb61 | 339 | |
5f9b6ced | 340 | run_command "ssh $target rm -f /tmp/$modtar"; |
2545eb61 SR |
341 | } |
342 | ||
6c5ee0be SR |
343 | sub check_buildlog { |
344 | my ($patch) = @_; | |
345 | ||
346 | my $buildlog = "$opt{TMP_DIR}/buildlog"; | |
347 | my @files = `git show $patch | diffstat -l`; | |
348 | ||
349 | open(IN, "git show $patch |") or | |
350 | dodie "failed to show $patch"; | |
351 | while (<IN>) { | |
352 | if (m,^--- a/(.*),) { | |
353 | chomp $1; | |
354 | $files[$#files] = $1; | |
355 | } | |
356 | } | |
357 | close(IN); | |
358 | ||
359 | open(IN, $buildlog) or dodie "Can't open $buildlog"; | |
360 | while (<IN>) { | |
361 | if (/^\s*(.*?):.*(warning|error)/) { | |
362 | my $err = $1; | |
363 | foreach my $file (@files) { | |
364 | my $fullpath = "$opt{BUILD_DIR}/$file"; | |
365 | if ($file eq $err || $fullpath eq $err) { | |
366 | dodie "$file built with warnings"; | |
367 | } | |
368 | } | |
369 | } | |
370 | } | |
371 | close(IN); | |
372 | } | |
373 | ||
2545eb61 SR |
374 | sub build { |
375 | my ($type) = @_; | |
5c42fc5b SR |
376 | my $defconfig = ""; |
377 | my $append = ""; | |
378 | ||
75c3fda7 | 379 | if ($type =~ /^useconfig:(.*)/) { |
5f9b6ced | 380 | run_command "cp $1 $opt{OUTPUT_DIR}/.config" or |
75c3fda7 | 381 | dodie "could not copy $1 to .config"; |
5f9b6ced | 382 | |
75c3fda7 SR |
383 | $type = "oldconfig"; |
384 | } | |
385 | ||
5c42fc5b SR |
386 | # old config can ask questions |
387 | if ($type eq "oldconfig") { | |
388 | $append = "yes ''|"; | |
75c3fda7 SR |
389 | |
390 | # allow for empty configs | |
391 | run_command "touch $opt{OUTPUT_DIR}/.config"; | |
392 | ||
5f9b6ced | 393 | run_command "mv $opt{OUTPUT_DIR}/.config $opt{OUTPUT_DIR}/config_temp" or |
5c42fc5b | 394 | dodie "moving .config"; |
2545eb61 | 395 | |
5f9b6ced | 396 | if (!$noclean && !run_command "$make mrproper") { |
5c42fc5b SR |
397 | dodie "make mrproper"; |
398 | } | |
2545eb61 | 399 | |
5f9b6ced | 400 | run_command "mv $opt{OUTPUT_DIR}/config_temp $opt{OUTPUT_DIR}/.config" or |
5c42fc5b | 401 | dodie "moving config_temp"; |
5c42fc5b SR |
402 | |
403 | } elsif (!$noclean) { | |
404 | unlink "$opt{OUTPUT_DIR}/.config"; | |
5f9b6ced | 405 | run_command "$make mrproper" or |
5c42fc5b | 406 | dodie "make mrproper"; |
5c42fc5b | 407 | } |
2545eb61 SR |
408 | |
409 | # add something to distinguish this build | |
5c42fc5b | 410 | open(OUT, "> $opt{OUTPUT_DIR}/localversion") or dodie("Can't make localversion file"); |
2545eb61 SR |
411 | print OUT "$opt{LOCALVERSION}\n"; |
412 | close(OUT); | |
413 | ||
5f9b6ced SR |
414 | if (defined($minconfig)) { |
415 | $defconfig = "KCONFIG_ALLCONFIG=$minconfig"; | |
2545eb61 SR |
416 | } |
417 | ||
5f9b6ced | 418 | run_command "$defconfig $append $make $type" or |
5c42fc5b | 419 | dodie "failed make config"; |
2545eb61 | 420 | |
6c5ee0be SR |
421 | # patch check will examine the log |
422 | if ($in_patchcheck) { | |
423 | $redirect = "$opt{TMP_DIR}/buildlog"; | |
424 | } | |
425 | ||
5f9b6ced | 426 | if (!run_command "$make $opt{BUILD_OPTIONS}") { |
6c5ee0be | 427 | undef $redirect; |
5f9b6ced SR |
428 | # bisect may need this to pass |
429 | return 1 if ($in_bisect); | |
5c42fc5b | 430 | dodie "failed build"; |
2545eb61 | 431 | } |
6c5ee0be | 432 | undef $redirect; |
5f9b6ced SR |
433 | |
434 | return 0; | |
2545eb61 SR |
435 | } |
436 | ||
75c3fda7 SR |
437 | sub reboot { |
438 | # try to reboot normally | |
5f9b6ced | 439 | if (!run_command "ssh $target reboot") { |
75c3fda7 SR |
440 | # nope? power cycle it. |
441 | run_command "$opt{POWER_CYCLE}"; | |
442 | } | |
443 | } | |
444 | ||
445 | sub halt { | |
5f9b6ced | 446 | if (!run_command "ssh $target halt" or defined($opt{"POWER_OFF"})) { |
75c3fda7 SR |
447 | # nope? the zap it! |
448 | run_command "$opt{POWER_OFF}"; | |
449 | } | |
450 | } | |
451 | ||
5f9b6ced SR |
452 | sub success { |
453 | my ($i) = @_; | |
454 | ||
455 | doprint "\n\n*******************************************\n"; | |
456 | doprint "*******************************************\n"; | |
457 | doprint "** SUCCESS!!!! **\n"; | |
458 | doprint "*******************************************\n"; | |
459 | doprint "*******************************************\n"; | |
460 | ||
461 | if ($i != $opt{"NUM_BUILDS"}) { | |
462 | reboot; | |
463 | doprint "Sleeping $opt{SLEEP_TIME} seconds\n"; | |
464 | sleep "$opt{SLEEP_TIME}"; | |
465 | } | |
466 | } | |
467 | ||
468 | sub get_version { | |
469 | # get the release name | |
470 | doprint "$make kernelrelease ... "; | |
471 | $version = `$make kernelrelease | tail -1`; | |
472 | chomp($version); | |
473 | doprint "$version\n"; | |
474 | } | |
475 | ||
5a391fbf SR |
476 | sub child_run_test { |
477 | my $failed; | |
478 | ||
479 | $failed = !run_command $run_test; | |
480 | exit $failed; | |
481 | } | |
482 | ||
483 | my $child_done; | |
484 | ||
485 | sub child_finished { | |
486 | $child_done = 1; | |
487 | } | |
488 | ||
489 | sub do_run_test { | |
490 | my $child_pid; | |
491 | my $child_exit; | |
492 | my $pid; | |
493 | my $line; | |
494 | my $full_line; | |
495 | my $bug = 0; | |
496 | my $fp = \*IN; | |
497 | ||
498 | $pid = open_console($fp); | |
499 | ||
500 | # read the monitor and wait for the system to calm down | |
501 | do { | |
502 | $line = wait_for_input($fp, 1); | |
503 | } while (defined($line)); | |
504 | ||
505 | $child_done = 0; | |
506 | ||
507 | $SIG{CHLD} = qw(child_finished); | |
508 | ||
509 | $child_pid = fork; | |
510 | ||
511 | child_run_test if (!$child_pid); | |
512 | ||
513 | $full_line = ""; | |
514 | ||
515 | do { | |
516 | $line = wait_for_input($fp, 1); | |
517 | if (defined($line)) { | |
518 | ||
519 | # we are not guaranteed to get a full line | |
520 | $full_line .= $line; | |
521 | ||
522 | if ($full_line =~ /call trace:/i) { | |
523 | $bug = 1; | |
524 | } | |
525 | ||
526 | if ($full_line =~ /Kernel panic -/) { | |
527 | $bug = 1; | |
528 | } | |
529 | ||
530 | if ($line =~ /\n/) { | |
531 | $full_line = ""; | |
532 | } | |
533 | } | |
534 | } while (!$child_done && !$bug); | |
535 | ||
536 | if ($bug) { | |
537 | doprint "Detected kernel crash!\n"; | |
538 | # kill the child with extreme prejudice | |
539 | kill 9, $child_pid; | |
540 | } | |
541 | ||
542 | waitpid $child_pid, 0; | |
543 | $child_exit = $?; | |
544 | ||
545 | close_console($fp, $pid); | |
546 | ||
547 | if ($bug || $child_exit) { | |
548 | return 1 if $in_bisect; | |
549 | dodie "test failed"; | |
550 | } | |
551 | return 0; | |
552 | } | |
553 | ||
5f9b6ced SR |
554 | sub run_bisect { |
555 | my ($type) = @_; | |
556 | ||
557 | my $failed; | |
558 | my $result; | |
559 | my $output; | |
560 | my $ret; | |
561 | ||
562 | ||
563 | if (defined($minconfig)) { | |
564 | $failed = build "useconfig:$minconfig"; | |
565 | } else { | |
566 | # ?? no config to use? | |
567 | $failed = build "oldconfig"; | |
568 | } | |
569 | ||
570 | if ($type ne "build") { | |
571 | dodie "Failed on build" if $failed; | |
572 | ||
573 | # Now boot the box | |
574 | get_grub_index; | |
575 | get_version; | |
576 | install; | |
577 | $failed = monitor; | |
578 | ||
579 | if ($type ne "boot") { | |
580 | dodie "Failed on boot" if $failed; | |
5a391fbf SR |
581 | |
582 | $failed = do_run_test; | |
5f9b6ced SR |
583 | } |
584 | } | |
585 | ||
586 | if ($failed) { | |
587 | $result = "bad"; | |
5a391fbf SR |
588 | |
589 | # reboot the box to a good kernel | |
590 | if ($type eq "boot") { | |
591 | reboot; | |
592 | doprint "sleep a little for reboot\n"; | |
593 | sleep $opt{"BISECT_SLEEP_TIME"}; | |
594 | } | |
5f9b6ced SR |
595 | } else { |
596 | $result = "good"; | |
597 | } | |
598 | ||
d6ce2a0b SR |
599 | # Are we looking for where it worked, not failed? |
600 | if ($reverse_bisect) { | |
601 | if ($failed) { | |
602 | $result = "good"; | |
603 | } else { | |
604 | $result = "bad"; | |
605 | } | |
606 | } | |
607 | ||
5f9b6ced SR |
608 | doprint "git bisect $result ... "; |
609 | $output = `git bisect $result 2>&1`; | |
610 | $ret = $?; | |
611 | ||
612 | logit $output; | |
613 | ||
614 | if ($ret) { | |
615 | doprint "FAILED\n"; | |
616 | dodie "Failed to git bisect"; | |
617 | } | |
618 | ||
619 | doprint "SUCCESS\n"; | |
5a391fbf | 620 | if ($output =~ m/^(Bisecting: .*\(roughly \d+ steps?\))\s+\[([[:xdigit:]]+)\]/) { |
5f9b6ced SR |
621 | doprint "$1 [$2]\n"; |
622 | } elsif ($output =~ m/^([[:xdigit:]]+) is the first bad commit/) { | |
623 | $bisect_bad = $1; | |
624 | doprint "Found bad commit... $1\n"; | |
625 | return 0; | |
5a391fbf SR |
626 | } else { |
627 | # we already logged it, just print it now. | |
628 | print $output; | |
5f9b6ced SR |
629 | } |
630 | ||
631 | ||
632 | return 1; | |
633 | } | |
634 | ||
635 | sub bisect { | |
636 | my ($i) = @_; | |
637 | ||
638 | my $result; | |
639 | ||
640 | die "BISECT_GOOD[$i] not defined\n" if (!defined($opt{"BISECT_GOOD[$i]"})); | |
641 | die "BISECT_BAD[$i] not defined\n" if (!defined($opt{"BISECT_BAD[$i]"})); | |
642 | die "BISECT_TYPE[$i] not defined\n" if (!defined($opt{"BISECT_TYPE[$i]"})); | |
643 | ||
644 | my $good = $opt{"BISECT_GOOD[$i]"}; | |
645 | my $bad = $opt{"BISECT_BAD[$i]"}; | |
646 | my $type = $opt{"BISECT_TYPE[$i]"}; | |
647 | ||
d6ce2a0b SR |
648 | if (defined($opt{"BISECT_REVERSE[$i]"}) && |
649 | $opt{"BISECT_REVERSE[$i]"} == 1) { | |
650 | doprint "Performing a reverse bisect (bad is good, good is bad!)\n"; | |
651 | $reverse_bisect = 1; | |
652 | } else { | |
653 | $reverse_bisect = 0; | |
654 | } | |
655 | ||
5f9b6ced SR |
656 | $in_bisect = 1; |
657 | ||
658 | run_command "git bisect start" or | |
659 | dodie "could not start bisect"; | |
660 | ||
661 | run_command "git bisect good $good" or | |
662 | dodie "could not set bisect good to $good"; | |
663 | ||
664 | run_command "git bisect bad $bad" or | |
665 | dodie "could not set bisect good to $bad"; | |
666 | ||
5a391fbf SR |
667 | # Can't have a test without having a test to run |
668 | if ($type eq "test" && !defined($run_test)) { | |
669 | $type = "boot"; | |
670 | } | |
671 | ||
5f9b6ced SR |
672 | do { |
673 | $result = run_bisect $type; | |
674 | } while ($result); | |
675 | ||
676 | run_command "git bisect log" or | |
677 | dodie "could not capture git bisect log"; | |
678 | ||
679 | run_command "git bisect reset" or | |
680 | dodie "could not reset git bisect"; | |
681 | ||
682 | doprint "Bad commit was [$bisect_bad]\n"; | |
683 | ||
684 | $in_bisect = 0; | |
685 | ||
686 | success $i; | |
687 | } | |
688 | ||
6c5ee0be SR |
689 | sub patchcheck { |
690 | my ($i) = @_; | |
691 | ||
692 | die "PATCHCHECK_START[$i] not defined\n" | |
693 | if (!defined($opt{"PATCHCHECK_START[$i]"})); | |
694 | die "PATCHCHECK_TYPE[$i] not defined\n" | |
695 | if (!defined($opt{"PATCHCHECK_TYPE[$i]"})); | |
696 | ||
697 | my $start = $opt{"PATCHCHECK_START[$i]"}; | |
698 | ||
699 | my $end = "HEAD"; | |
700 | if (defined($opt{"PATCHCHECK_END[$i]"})) { | |
701 | $end = $opt{"PATCHCHECK_END[$i]"}; | |
702 | } | |
703 | ||
704 | my $type = $opt{"PATCHCHECK_TYPE[$i]"}; | |
705 | ||
706 | # Can't have a test without having a test to run | |
707 | if ($type eq "test" && !defined($run_test)) { | |
708 | $type = "boot"; | |
709 | } | |
710 | ||
711 | open (IN, "git log --pretty=oneline $end|") or | |
712 | dodie "could not get git list"; | |
713 | ||
714 | my @list; | |
715 | ||
716 | while (<IN>) { | |
717 | chomp; | |
718 | $list[$#list+1] = $_; | |
719 | last if (/^$start/); | |
720 | } | |
721 | close(IN); | |
722 | ||
723 | if ($list[$#list] !~ /^$start/) { | |
724 | dodie "SHA1 $start not found"; | |
725 | } | |
726 | ||
727 | # go backwards in the list | |
728 | @list = reverse @list; | |
729 | ||
730 | my $save_clean = $noclean; | |
731 | ||
732 | $in_patchcheck = 1; | |
733 | foreach my $item (@list) { | |
734 | my $sha1 = $item; | |
735 | $sha1 =~ s/^([[:xdigit:]]+).*/$1/; | |
736 | ||
737 | doprint "\nProcessing commit $item\n\n"; | |
738 | ||
739 | run_command "git checkout $sha1" or | |
740 | die "Failed to checkout $sha1"; | |
741 | ||
742 | # only clean on the first and last patch | |
743 | if ($item eq $list[0] || | |
744 | $item eq $list[$#list]) { | |
745 | $noclean = $save_clean; | |
746 | } else { | |
747 | $noclean = 1; | |
748 | } | |
749 | ||
750 | if (defined($minconfig)) { | |
751 | build "useconfig:$minconfig"; | |
752 | } else { | |
753 | # ?? no config to use? | |
754 | build "oldconfig"; | |
755 | } | |
756 | ||
757 | check_buildlog $sha1; | |
758 | ||
759 | next if ($type eq "build"); | |
760 | ||
761 | get_grub_index; | |
762 | get_version; | |
763 | install; | |
764 | monitor; | |
765 | ||
766 | next if ($type eq "boot"); | |
767 | do_run_test; | |
768 | } | |
769 | $in_patchcheck = 0; | |
770 | success $i; | |
771 | } | |
772 | ||
2545eb61 SR |
773 | read_config $ARGV[0]; |
774 | ||
775 | # mandatory configs | |
776 | die "MACHINE not defined\n" if (!defined($opt{"MACHINE"})); | |
777 | die "SSH_USER not defined\n" if (!defined($opt{"SSH_USER"})); | |
778 | die "BUILD_DIR not defined\n" if (!defined($opt{"BUILD_DIR"})); | |
779 | die "OUTPUT_DIR not defined\n" if (!defined($opt{"OUTPUT_DIR"})); | |
780 | die "BUILD_TARGET not defined\n" if (!defined($opt{"BUILD_TARGET"})); | |
75c3fda7 | 781 | die "TARGET_IMAGE not defined\n" if (!defined($opt{"TARGET_IMAGE"})); |
2545eb61 SR |
782 | die "POWER_CYCLE not defined\n" if (!defined($opt{"POWER_CYCLE"})); |
783 | die "CONSOLE not defined\n" if (!defined($opt{"CONSOLE"})); | |
784 | die "LOCALVERSION not defined\n" if (!defined($opt{"LOCALVERSION"})); | |
785 | die "GRUB_MENU not defined\n" if (!defined($opt{"GRUB_MENU"})); | |
786 | ||
787 | chdir $opt{"BUILD_DIR"} || die "can't change directory to $opt{BUILD_DIR}"; | |
788 | ||
789 | $target = "$opt{SSH_USER}\@$opt{MACHINE}"; | |
790 | ||
791 | doprint "\n\nSTARTING AUTOMATED TESTS\n"; | |
792 | ||
2545eb61 SR |
793 | |
794 | $make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}"; | |
795 | ||
5a391fbf SR |
796 | sub set_build_option { |
797 | my ($name, $i) = @_; | |
2545eb61 | 798 | |
5a391fbf | 799 | my $option = "$name\[$i\]"; |
5c42fc5b | 800 | |
5a391fbf SR |
801 | if (defined($opt{$option})) { |
802 | return $opt{$option}; | |
5f9b6ced SR |
803 | } |
804 | ||
5a391fbf SR |
805 | if (defined($opt{$name})) { |
806 | return $opt{$name}; | |
2545eb61 SR |
807 | } |
808 | ||
5a391fbf SR |
809 | return undef; |
810 | } | |
811 | ||
812 | # First we need to do is the builds | |
813 | for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) { | |
814 | my $type = "BUILD_TYPE[$i]"; | |
815 | ||
1a5cfce3 SR |
816 | if (!defined($opt{$type})) { |
817 | $opt{$type} = $opt{"DEFAULT_BUILD_TYPE"}; | |
818 | } | |
819 | ||
5a391fbf SR |
820 | $noclean = set_build_option("BUILD_NOCLEAN", $i); |
821 | $minconfig = set_build_option("MIN_CONFIG", $i); | |
822 | $run_test = set_build_option("TEST", $i); | |
823 | ||
2545eb61 SR |
824 | doprint "\n\n"; |
825 | doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n"; | |
826 | ||
6c5ee0be SR |
827 | my $checkout = $opt{"CHECKOUT[$i]"}; |
828 | if (defined($checkout)) { | |
829 | run_command "git checkout $checkout" or | |
830 | die "failed to checkout $checkout"; | |
831 | } | |
832 | ||
5f9b6ced SR |
833 | if ($opt{$type} eq "bisect") { |
834 | bisect $i; | |
835 | next; | |
6c5ee0be SR |
836 | } elsif ($opt{$type} eq "patchcheck") { |
837 | patchcheck $i; | |
838 | next; | |
2545eb61 | 839 | } |
2545eb61 | 840 | |
5f9b6ced SR |
841 | if ($opt{$type} ne "nobuild") { |
842 | build $opt{$type}; | |
2545eb61 SR |
843 | } |
844 | ||
5f9b6ced SR |
845 | get_grub_index; |
846 | get_version; | |
2545eb61 | 847 | install; |
2545eb61 | 848 | monitor; |
5a391fbf SR |
849 | |
850 | if (defined($run_test)) { | |
851 | do_run_test; | |
852 | } | |
853 | ||
5f9b6ced | 854 | success $i; |
2545eb61 SR |
855 | } |
856 | ||
5c42fc5b | 857 | if ($opt{"POWEROFF_ON_SUCCESS"}) { |
75c3fda7 | 858 | halt; |
1a5cfce3 | 859 | } elsif ($opt{"REBOOT_ON_SUCCESS"}) { |
75c3fda7 | 860 | reboot; |
5c42fc5b | 861 | } |
75c3fda7 | 862 | |
2545eb61 | 863 | exit 0; |