X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Futils%2Fpintos;h=214e05198a17edd13271b024e601149f3a316c73;hb=c6332756cab9a04ada8f26bcd22e1b8f37dd7cc3;hp=8721db313042bb35438dd1cf20856aa0f816c7e2;hpb=4ebf33908a571a7cde93fe618902b044e3633cdf;p=pintos-anon diff --git a/src/utils/pintos b/src/utils/pintos index 8721db3..214e051 100755 --- a/src/utils/pintos +++ b/src/utils/pintos @@ -7,7 +7,8 @@ use File::Temp 'tempfile'; use Getopt::Long qw(:config bundling); # Command-line options. -our ($sim); # Simulator: bochs, qemu, or gsx. +our ($start_time) = time (); +our ($sim) = $ENV{PINTOSSIM}; # Simulator: bochs, qemu, or gsx. our ($debug) = "none"; # Debugger: none, monitor, or gdb. our ($mem) = 4; # Physical RAM in MB. our ($serial_out) = 1; # Send output to serial port? @@ -19,7 +20,7 @@ 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. our (@kernel_args); # Arguments to pass to kernel. -our (%disks) = (OS => {FILENAME => 'os.dsk'}, # Disks to give VM. +our (%disks) = (OS => {DEF_FN => 'os.dsk'}, # Disks to give VM. FS => {DEF_FN => 'fs.dsk'}, SCRATCH => {DEF_FN => 'scratch.dsk'}, SWAP => {DEF_FN => 'swap.dsk'}); @@ -84,6 +85,9 @@ sub parse_command_line { $sim = "bochs" if !defined $sim; $debug = "none" if !defined $debug; $vga = "window" if !defined $vga; + + print "warning: -T or --timeout should not be used with --$debug\n" + if defined ($timeout) && $debug ne 'none'; } # usage($exitcode). @@ -111,7 +115,8 @@ Display options: (default is both VGA and serial) Timing options: (Bochs only) -j SEED Randomize timer interrupts -r, --realtime Use realistic, not reproducible, timings - -T, --timeout=N Time out and kill Pintos after N seconds + -T, --timeout=N Kill Pintos after N seconds CPU time or N*load_avg + seconds wall-clock time (whichever comes first) Configuration options: -m, --mem=N Give Pintos N MB physical RAM (default: 4) File system commands (for `run' command): @@ -125,6 +130,8 @@ Disk options: (name an existing FILE or specify SIZE in MB for a temp disk) --swap-disk=FILE|SIZE Set swap disk file (default: swap.dsk) Other options: -h, --help Display this help message. +Environment variables: + PINTOSSIM Select default simulator. EOF exit $exitcode; } @@ -193,9 +200,13 @@ sub set_as { sub find_disks { for my $disk (values %disks) { # If there's no assigned file name but the default file exists, - # assign the default file. - $disk->{FILENAME} = $disk->{DEF_FN} - if !defined ($disk->{FILENAME}) && -e $disk->{DEF_FN}; + # try to assign a default file name. + if (!defined ($disk->{FILENAME})) { + for my $try_fn ($disk->{DEF_FN}, "build/" . $disk->{DEF_FN}) { + $disk->{FILENAME} = $try_fn, last + if -e $try_fn; + } + } # If there's no file name, we're done. next if !defined ($disk->{FILENAME}); @@ -208,10 +219,8 @@ sub find_disks { my ($mb) = $disk->{FILENAME}; undef $disk->{FILENAME}; - my ($cylinder) = 1024 * 504; - my ($bytes) = $mb * ($cylinder * 2); - $bytes = int (($bytes + $cylinder - 1) / $cylinder) * $cylinder; - extend_disk ($disk, $bytes); + my ($cyl_size) = 512 * 16 * 63; + extend_disk ($disk, ceil ($mb * 2) * $cyl_size); } else { # The file must exist and have nonzero size. -e $disk->{FILENAME} or die "$disk->{FILENAME}: stat: $!\n"; @@ -220,6 +229,7 @@ sub find_disks { } # Warn about (potentially) missing disks. + die "Cannot find OS disk\n" if !defined $disks{OS}{FILENAME}; if (my ($project) = `pwd` =~ /\b(threads|userprog|vm|filesys)\b/) { if ((grep ($project eq $_, qw (userprog vm filesys))) && !defined ($disks{FS}{FILENAME})) { @@ -359,26 +369,22 @@ sub run_vm { # Runs Bochs. sub run_bochs { # Select Bochs binary based on the chosen debugger. - my ($bin); - if ($debug eq 'none') { - $bin = 'bochs'; - } elsif ($debug eq 'monitor') { - $bin = 'bochs-dbg'; - } elsif ($debug eq 'gdb') { - $bin = 'bochs-gdb'; - } + my ($bin) = $debug eq 'monitor' ? 'bochs-dbg' : 'bochs'; # Write bochsrc.txt configuration file. open (BOCHSRC, ">", "bochsrc.txt") or die "bochsrc.txt: create: $!\n"; print BOCHSRC <{FILENAME}; if (defined $file) { - my (%geom) = disk_geometry ($file); + my (%geom) = disk_geometry ($disk); print BOCHSRC "$device: type=disk, path=$file, mode=flat, "; print BOCHSRC "cylinders=$geom{C}, heads=$geom{H}, spt=$geom{S}, "; print BOCHSRC "translation=none\n"; @@ -482,7 +490,8 @@ serial0.fileName = "pintos.out" EOF for (my ($i) = 0; $i < 4; $i++) { - my ($dsk) = $disks_by_iface[$i]{FILENAME}; + my ($disk) = $disks_by_iface[$i]; + my ($dsk) = $disk->{FILENAME}; next if !defined $dsk; my ($pln) = $dsk; @@ -497,7 +506,7 @@ $device.deviceType = "plainDisk" $device.fileName = "$pln" EOF - my (%geom) = disk_geometry ($dsk); + my (%geom) = disk_geometry ($disk); open (PLN, ">", $pln) or die "$pln: create: $!\n"; print PLN <{FILENAME}; my ($size) = -s $file; die "$file: stat: $!\n" if !defined $size; die "$file: size not a multiple of 512 bytes\n" if $size % 512; - my ($cylinders) = int ($size / (512 * 16 * 63)); - $cylinders++ if $size % (512 * 16 * 63); + my ($cyl_size) = 512 * 16 * 63; + my ($cylinders) = ceil ($size / $cyl_size); + extend_disk ($disk, $cylinders * $cyl_size) if $size % $cyl_size; return (CAPACITY => $size / 512, C => $cylinders, @@ -652,16 +663,22 @@ sub xsystem { die "fork: $!\n"; } elsif (!$pid) { # Running in child process. - exec (@_); - exit (1); + 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"); }; - alarm ($timeout) if defined ($timeout); + alarm ($timeout * get_load_average () + 1) if defined ($timeout); waitpid ($pid, 0); alarm (0); + + if (WIFSIGNALED ($?) && WTERMSIG ($?) == SIGVTALRM ()) { + seek (STDOUT, 0, 2); + print "\nTIMEOUT after $timeout seconds of host CPU time\n"; + exit 0; + } + return $?; } } @@ -669,10 +686,11 @@ sub xsystem { # relay_signal($pid, $signal) # # Relays $signal to $pid and then reinvokes it for us with the default -# handler. +# handler. Also cleans up temporary files. sub relay_signal { my ($pid, $signal) = @_; kill $signal, $pid; + File::Temp::cleanup(); $SIG{$signal} = 'DEFAULT'; kill $signal, getpid (); } @@ -682,10 +700,49 @@ sub relay_signal { # Interrupts $pid and dies with a timeout error message. sub timeout { my ($pid) = @_; - relay_signal ($pid, "INT"); + kill "INT", $pid; + waitpid ($pid, 0); + seek (STDOUT, 0, 2); my ($load_avg) = `uptime` =~ /(load average:.*)$/i; - print "TIMEOUT after $timeout seconds"; + print "\nTIMEOUT after ", time () - $start_time, + " seconds of wall-clock time"; print " - $load_avg" if defined $load_avg; print "\n"; - exit (2); + exit 0; +} + +# Returns the system load average over the last minute. +# If the load average is less than 1.0 or cannot be determined, returns 1.0. +sub get_load_average { + my ($avg) = `uptime` =~ /load average:\s*([^,]+),/; + return $avg >= 1.0 ? $avg : 1.0; +} + +# Calls setitimer to set a timeout, then execs what was passed to us. +sub exec_setitimer { + if (defined $timeout) { + if ($ ge 5.8.0) { + eval " + use Time::HiRes qw(setitimer ITIMER_VIRTUAL); + setitimer (ITIMER_VIRTUAL, $timeout, 0); + "; + } else { + { exec ("setitimer-helper", $timeout, @_); }; + exit 1 if !$!{ENOENT}; + print STDERR "warning: setitimer-helper is not installed, so ", + "CPU time limit will not be enforced\n"; + } + } + exec (@_); + exit (1); +} + +sub SIGVTALRM { + use Config; + my $i = 0; + foreach my $name (split(' ', $Config{sig_name})) { + return $i if $name eq 'VTALRM'; + $i++; + } + return 0; }