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?
$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).
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):
--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;
}
# 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 <<EOF;
romimage: file=\$BXSHARE/BIOS-bochs-latest, address=0xf0000
-vgaromimage: \$BXSHARE/VGABIOS-lgpl-latest
-boot: c
-ips: 1000000
+vgaromimage: file=\$BXSHARE/VGABIOS-lgpl-latest
+boot: disk
+cpu: ips=1000000
megs: $mem
log: bochsout.txt
panic: action=fatal
EOF
- print BOCHSRC "clock: sync=", $realtime ? 'realtime' : 'none', "time0=0\n";
+ print BOCHSRC "gdbstub: enabled=1\n" if $debug eq 'gdb';
+ print BOCHSRC "clock: sync=", $realtime ? 'realtime' : 'none',
+ " time0=0\n";
print_bochs_disk_line ("ata0-master", 0);
print_bochs_disk_line ("ata0-slave", 1);
if (defined ($disks_by_iface[2]{FILENAME})
print_bochs_disk_line ("ata1-slave", 3);
}
if ($vga ne 'terminal') {
- print BOCHSRC "com1: enabled=1, dev=/dev/stdout\n" if $serial_out;
+ print BOCHSRC "com1: enabled=1, mode=file, dev=/dev/stdout\n"
+ if $serial_out;
print BOCHSRC "display_library: nogui\n" if $vga eq 'none';
} else {
print BOCHSRC "display_library: term\n";
push (@cmd, $option, $disks_by_iface[$iface]{FILENAME})
if defined $disks_by_iface[$iface]{FILENAME};
}
- push (@cmd, '-m', $mem, '-nics', '0');
+ push (@cmd, '-m', $mem);
push (@cmd, '-nographic') if $vga eq 'none';
push (@cmd, '-serial', 'stdio') if $serial_out && $vga ne 'none';
push (@cmd, '-S') if $debug eq 'monitor';
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 $?;
}
}
# 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 ();
}
waitpid ($pid, 0);
seek (STDOUT, 0, 2);
my ($load_avg) = `uptime` =~ /(load average:.*)$/i;
- print "\nTIMEOUT after $timeout seconds";
+ print "\nTIMEOUT after ", time () - $start_time,
+ " seconds of wall-clock time";
print " - $load_avg" if defined $load_avg;
print "\n";
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 ($\16 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;
+}