From f14d80c05e67af1545779f038d9f47dec2188fc3 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sat, 27 May 2006 20:21:09 +0000 Subject: [PATCH] Add "-k" option to pintos script. When used, pintos will scan the serial output for key words like "Kernel PANIC" and, if found, terminate the run. This is useful for making sure that things like triple faults or recursive panics don't waste a lot of time by finally terminating only upon a timeout that might be as much as 10 minutes. Add "-k" to the command line used in the test scripts. Also, pintos now saves and restores the terminal settings across the call to the simulator. This is because qemu turns off local echo and doesn't restore it if killed by a signal. --- src/tests/Make.tests | 2 +- src/tests/filesys/extended/Make.tests | 2 +- src/utils/pintos | 97 ++++++++++++++++++++++----- 3 files changed, 83 insertions(+), 18 deletions(-) diff --git a/src/tests/Make.tests b/src/tests/Make.tests index b98a75d..230e03a 100644 --- a/src/tests/Make.tests +++ b/src/tests/Make.tests @@ -47,7 +47,7 @@ $(foreach prog,$(PROGS),$(eval $(prog).output: $(prog))) $(foreach test,$(TESTS),$(eval $(test).output: $($(test)_PUTFILES))) $(foreach test,$(TESTS),$(eval $(test).output: TEST = $(test))) -TESTCMD = pintos -v -T $(TIMEOUT) +TESTCMD = pintos -v -k -T $(TIMEOUT) TESTCMD += $(SIMULATOR) TESTCMD += $(PINTOSOPTS) ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog) diff --git a/src/tests/filesys/extended/Make.tests b/src/tests/filesys/extended/Make.tests index 1aeb8dd..2ba4d76 100644 --- a/src/tests/filesys/extended/Make.tests +++ b/src/tests/filesys/extended/Make.tests @@ -27,7 +27,7 @@ tests/filesys/extended/syn-rw_PUTFILES += tests/filesys/extended/child-syn-rw GETTIMEOUT = 60 -GETCMD = pintos -v -T $(GETTIMEOUT) +GETCMD = pintos -v -k -T $(GETTIMEOUT) GETCMD += $(PINTOSOPTS) GETCMD += $(SIMULATOR) GETCMD += --fs-disk=$(FSDISK) diff --git a/src/utils/pintos b/src/utils/pintos index f0ab60c..856ebfa 100755 --- a/src/utils/pintos +++ b/src/utils/pintos @@ -16,6 +16,7 @@ our ($vga); # VGA output: window, terminal, or none. our ($jitter); # Seed for random timer interrupts, if set. our ($realtime); # Synchronize timer interrupts with real time? our ($timeout); # Maximum runtime in seconds, if set. +our ($kill_on_failure); # Abort quickly on test failure? our (@puts); # Files to copy into the VM. our (@gets); # Files to copy out of the VM. our ($as_ref); # Reference to last addition to @gets or @puts. @@ -58,7 +59,9 @@ sub parse_command_line { "m|memory=i" => \$mem, "j|jitter=i" => sub { set_jitter (@_) }, "r|realtime" => sub { set_realtime () }, + "T|timeout=i" => \$timeout, + "k|kill-on-failure" => \$kill_on_failure, "v|no-vga" => sub { set_vga ('none'); }, "s|no-serial" => sub { $serial_out = 0; }, @@ -86,8 +89,11 @@ sub parse_command_line { $debug = "none" if !defined $debug; $vga = "window" if !defined $vga; - print "warning: -T or --timeout should not be used with --$debug\n" + undef $timeout, print "warning: disabling timeout with --$debug\n" if defined ($timeout) && $debug ne 'none'; + + print "warning: enabling serial output for -k or --kill-on-failure\n" + if $kill_on_failure && !$serial_out; } # usage($exitcode). @@ -115,8 +121,11 @@ Display options: (default is both VGA and serial) Timing options: (Bochs only) -j SEED Randomize timer interrupts -r, --realtime Use realistic, not reproducible, timings +Testing options: -T, --timeout=N Kill Pintos after N seconds CPU time or N*load_avg seconds wall-clock time (whichever comes first) + -k, --kill-on-failure Kill Pintos a few seconds after a kernel or user + panic, test failure, or triple fault Configuration options: -m, --mem=N Give Pintos N MB physical RAM (default: 4) File system commands (for `run' command): @@ -471,6 +480,7 @@ sub run_gsx { gsx_unsup ("--no-vga") if $vga eq 'none'; gsx_unsup ("--terminal") if $vga eq 'terminal'; gsx_unsup ("--jitter") if defined $jitter; + gsx_unsup ("--kill-on-failure") if defined $kill_on_failure; unlink ("pintos.out"); @@ -656,21 +666,69 @@ sub run_command { # Relays common signals to the subprocess. # If $timeout is set then the subprocess will be killed after that long. sub xsystem { + # qemu turns off local echo and does not restore it if killed by a signal. + # We compensate by restoring it ourselves. + my $cleanup = sub {}; + if (isatty (0)) { + my $termios = POSIX::Termios->new; + $termios->getattr (0); + $cleanup = sub { $termios->setattr (0, &POSIX::TCSANOW); } + } + + # Create pipe for filtering output. + pipe (my $in, my $out) or die "pipe: $!\n" if $kill_on_failure; + my ($pid) = fork; if (!defined ($pid)) { # Fork failed. die "fork: $!\n"; } elsif (!$pid) { # Running in child process. + dup2 (fileno ($out), STDOUT_FILENO) or die "dup2: $!\n" + if $kill_on_failure; exec_setitimer (@_); } else { # Running in parent process. - local $SIG{ALRM} = sub { timeout ($pid); }; - local $SIG{INT} = sub { relay_signal ($pid, "INT"); }; - local $SIG{TERM} = sub { relay_signal ($pid, "TERM"); }; + close $out if $kill_on_failure; + + my ($cause); + local $SIG{ALRM} = sub { timeout ($pid, $cause, $cleanup); }; + local $SIG{INT} = sub { relay_signal ($pid, "INT", $cleanup); }; + local $SIG{TERM} = sub { relay_signal ($pid, "TERM", $cleanup); }; alarm ($timeout * get_load_average () + 1) if defined ($timeout); - waitpid ($pid, 0); + + if ($kill_on_failure) { + # Filter output. + my ($buf) = ""; + my ($boots) = 0; + while (waitpid ($pid, WNOHANG) == 0) { + # Read and print out pipe data. + my ($len) = length ($buf); + waitpid ($pid, 0), last + if sysread ($in, $buf, 4096, $len) <= 0; + print STDOUT substr ($buf, $len); + + # Remove full lines from $buf and scan them for keywords. + while ((my $idx = index ($buf, "\n")) >= 0) { + local $_ = substr ($buf, 0, $idx + 1, ''); + next if defined ($cause); + if (/(Kernel PANIC|User process ABORT)/ ) { + $cause = "\L$1\E"; + alarm (5); + } elsif (/Pintos booting/ && ++$boots > 1) { + $cause = "triple fault"; + alarm (5); + } elsif (/FAILED/) { + $cause = "test failure"; + alarm (5); + } + } + } + } else { + waitpid ($pid, 0); + } alarm (0); + &$cleanup (); if (WIFSIGNALED ($?) && WTERMSIG ($?) == SIGVTALRM ()) { seek (STDOUT, 0, 2); @@ -682,31 +740,38 @@ sub xsystem { } } -# relay_signal($pid, $signal) +# relay_signal($pid, $signal, &$cleanup) # # Relays $signal to $pid and then reinvokes it for us with the default -# handler. Also cleans up temporary files. +# handler. Also cleans up temporary files and invokes $cleanup. sub relay_signal { - my ($pid, $signal) = @_; + my ($pid, $signal, $cleanup) = @_; kill $signal, $pid; File::Temp::cleanup(); + &$cleanup (); $SIG{$signal} = 'DEFAULT'; kill $signal, getpid (); } -# timeout($pid) +# timeout($pid, $cause, &$cleanup) # -# Interrupts $pid and dies with a timeout error message. +# Interrupts $pid and dies with a timeout error message, +# after invoking $cleanup. sub timeout { - my ($pid) = @_; + my ($pid, $cause, $cleanup) = @_; kill "INT", $pid; waitpid ($pid, 0); + &$cleanup (); seek (STDOUT, 0, 2); - my ($load_avg) = `uptime` =~ /(load average:.*)$/i; - print "\nTIMEOUT after ", time () - $start_time, - " seconds of wall-clock time"; - print " - $load_avg" if defined $load_avg; - print "\n"; + if (!defined ($cause)) { + my ($load_avg) = `uptime` =~ /(load average:.*)$/i; + print "\nTIMEOUT after ", time () - $start_time, + " seconds of wall-clock time"; + print " - $load_avg" if defined $load_avg; + print "\n"; + } else { + print "Simulation terminated due to $cause.\n"; + } exit 0; } -- 2.30.2