445 lines
12 KiB
Perl
Executable File
445 lines
12 KiB
Perl
Executable File
#! /usr/bin/perl -W
|
|
#
|
|
# Run package tests using Fiasco-UX
|
|
#
|
|
#
|
|
# Adam Lackorzynski <adam@os.inf.tu-dresden.de>
|
|
# Ronald Aigner <ra3@os.inf.tu-dresden.de>
|
|
#
|
|
# This script is derived from the autocheck script which can be found in
|
|
# kernel/fiasco/tools. It has been slightly modified to suit the demands of
|
|
# the package tests.
|
|
#
|
|
|
|
# make it pass for now
|
|
#exit 0;
|
|
|
|
use strict;
|
|
use Getopt::Long;
|
|
|
|
my $FIASCOUX;
|
|
my $FIASCOUX_PARAM;
|
|
my $L4DIR;
|
|
my $OBJ_BASE;
|
|
my $OBJDIR = 'OBJ-x86_586-l4f';
|
|
my $EXPECTED_OUT = 'expected.txt';
|
|
my $TMP_OUT = 'tmp_out.txt';
|
|
my $PKGNAME;
|
|
my $COMPARE_CMD = 'diff -u $EXPECTED_OUT $TMP_OUT';
|
|
|
|
my $ALARM_TIMEOUT = 60; # in seconds
|
|
|
|
my %templs = (
|
|
roottask => '%s/pkg/roottask/server/src/%s/roottask',
|
|
sigma0 => '%s/pkg/sigma0/server/src/%s/sigma0',
|
|
);
|
|
|
|
my $Verbose = 0;
|
|
my $Quiet = 0;
|
|
my $Generate = 0;
|
|
my $Memory = 32;
|
|
my $Plainrun = 0;
|
|
my $No_fb = 0;
|
|
my $Use_Symbols = 0;
|
|
my $Use_Lines = 0;
|
|
|
|
my %progs;
|
|
my %results;
|
|
my %output;
|
|
my @Baseservers;
|
|
my $Client;
|
|
my $Server;
|
|
my $Fiasco_Dir;
|
|
|
|
# filehandle for fiasco output
|
|
my $Input;
|
|
my $Output;
|
|
|
|
my $Exit_code = 0;
|
|
my $pid = 0;
|
|
|
|
my $FILTER_LEVEL_FIASCO = 0;
|
|
my $FILTER_LEVEL_ROOTTASK = 1;
|
|
my $FILTER_LEVEL_USER = 2;
|
|
my $Filter_state = $FILTER_LEVEL_FIASCO;
|
|
my $Filter_level = $FILTER_LEVEL_USER;
|
|
|
|
sub usage() {
|
|
print <<EOU;
|
|
$0 [options] -c <client> -s <server>
|
|
|
|
--l4dir, -l path Path to an L4 directory
|
|
--fiascoux, -f file Path to the Fiasco-UX binary
|
|
--fiascoux_param, -p params Parameters for Fiasco-UX
|
|
--memory, -m megabyte Number of Megabytes used by Fiasco. (default: 32)
|
|
--objdir, -O objdir Object dir, currently: $OBJDIR
|
|
--verbose, -v Be verbose (e.g. show output of L4 apps)
|
|
--quiet, -q Tell nothing, just set the exit code
|
|
--generate Generate the output file instead of comparing to it
|
|
--server, -s file The server to be tested
|
|
--client, -c file The file to run as test-application
|
|
--base, -b file Additional base servers required by the application
|
|
Can be a comma seperated list. (Only specify the
|
|
binary names!) (default: log, names, dm_phys)
|
|
--timeout, -t timeout Time in seconds to wait before shooting down a
|
|
runaway fiasco. (default: 60)
|
|
--expectfail Expect failure. Return 0 on failure, 1 on success.
|
|
|
|
Environment variables:
|
|
L4DIR Path to an L4 directory
|
|
OBJ_BASE Path to build directory
|
|
FIASCOUX Path to the Fiasco-UX binary
|
|
EXPECTED_OUT Filename of the file containing the expected output
|
|
TMP_OUT Filename of the tempfile containing Fiasco's output
|
|
COMPARE_CMD Command to compare output and expected output
|
|
|
|
Notes:
|
|
Environment variables override options!
|
|
EOU
|
|
}
|
|
|
|
##
|
|
# Check if L4DIR looks like an L4 directory
|
|
sub check_for_l4dir() {
|
|
unless (-d "$L4DIR/pkg/l4sys/include" ||
|
|
-d "$L4DIR/../kernel/fiasco/src/kern/ux") {
|
|
die "$L4DIR doesn't look like an L4 directory!";
|
|
}
|
|
}
|
|
|
|
##
|
|
# Just check if the supplied binary in $FIASCOUX is really
|
|
# a Fiasco UX version. Native versions will just segfault.
|
|
sub check_for_fiasco_ux() {
|
|
|
|
unless ((-x $FIASCOUX && -f $FIASCOUX) ||
|
|
(-l $FIASCOUX && -x readlink $FIASCOUX && -f readlink $FIASCOUX)) {
|
|
die "$FIASCOUX: Does not exist or isn't an executable file";
|
|
}
|
|
|
|
system("$FIASCOUX -h >/dev/null 2>&1");
|
|
die "$FIASCOUX doesn't seem to be a UX version." if $?;
|
|
|
|
$FIASCOUX =~ /(.*)\/([^\/]*)$/;
|
|
$Fiasco_Dir = $1;
|
|
}
|
|
|
|
# check for user app in binary path of L4
|
|
sub check_user_app($)
|
|
{
|
|
my $app_with_args = shift;
|
|
|
|
# extract arch and api from OBJ-dir
|
|
$OBJDIR =~ /OBJ-([^-]*)-(.*)/;
|
|
my $arch = $1;
|
|
my $api = $2;
|
|
|
|
my @check = split(/\s+/, $app_with_args);
|
|
my $app = shift @check;
|
|
|
|
my $p = sprintf "%s/bin/%s/%s/%s", $OBJ_BASE, $arch, $api, $app;
|
|
die "There's no $p!" unless -f $p;
|
|
|
|
return $p.'\\ '.join('\\ ', @check) if @check;
|
|
$p;
|
|
}
|
|
|
|
##
|
|
# Check for userland (roottask, sigma0, ...)
|
|
sub check_for_userland() {
|
|
foreach my $t (keys %templs) {
|
|
my $p = sprintf $templs{$t}, $OBJ_BASE, $OBJDIR;
|
|
die "There's no $p!" unless -f $p;
|
|
$progs{$t} = $p;
|
|
}
|
|
|
|
# check for base-servers
|
|
my @servers = @Baseservers;
|
|
@Baseservers = {};
|
|
if (scalar(@servers) eq 0) {
|
|
@servers = ( "log", "names", "dm_phys" );
|
|
}
|
|
|
|
# iterate over servers and test if they exist in the bin dir
|
|
foreach my $s (@servers) {
|
|
push @Baseservers, check_user_app($s);
|
|
}
|
|
|
|
# test client and server
|
|
push @Baseservers, check_user_app($Client) if defined($Client);
|
|
push @Baseservers, check_user_app($Server);
|
|
}
|
|
|
|
##
|
|
# Does the comparison of the output
|
|
sub run_diff {
|
|
print STDERR "Running compare command \"$COMPARE_CMD\".\n" if $Verbose;
|
|
open(TMP, "$COMPARE_CMD |") or die "Cannot run diff: $!";
|
|
my @diff = <TMP>;
|
|
close TMP;
|
|
|
|
# reset console in interactive mode
|
|
system "if tty -s; then stty echo icrnl icanon; fi";
|
|
|
|
# check if there are differences between expected and generated output
|
|
if (@diff == 0) {
|
|
print STDERR "Output of test in $PKGNAME ok.\n";
|
|
return $Exit_code;
|
|
}
|
|
|
|
print STDERR "Test in $PKGNAME generated unexpected output:\n";
|
|
@diff = splice @diff, 1000 if @diff > 1000;
|
|
print STDERR @diff, "\n";
|
|
return 1 - $Exit_code;
|
|
}
|
|
|
|
##
|
|
# Called if second alarm signal received
|
|
#
|
|
# Now all the output of Fiasco-UX should be through and we can savely kill
|
|
# Fiasco-UX. We then check the generated output and terminate ourselves.
|
|
sub got_sig_alarm_second {
|
|
print STDERR "Timeout for flushing Fiasco-UX output\n" if $Verbose;
|
|
print STDERR "Sending SIGKILL to $pid and diff output\n" if $Verbose;
|
|
|
|
kill KILL => $pid;
|
|
alarm 0;
|
|
|
|
# some sanity checks
|
|
if ( $Filter_state < $FILTER_LEVEL_USER ) {
|
|
print $Output "\n";
|
|
print $Output "User tasks did not start. Maybe overlapping modules?\n";
|
|
print $Output "Run \"" . fiascoux_cmdline() . "\" manually to check.\n";
|
|
}
|
|
close $Output;
|
|
|
|
# in generate mode, simply return
|
|
if ($Generate) {
|
|
system "stty echo";
|
|
exit 0;
|
|
}
|
|
|
|
exit run_diff();
|
|
}
|
|
|
|
##
|
|
# Called if first alarm signal received
|
|
#
|
|
# To flush Fiasco Output we send it a SIGINT (^C). Then we set up a second
|
|
# timeout and return, so the filter can process the output which we forced to
|
|
# be flushed.
|
|
sub got_sig_alarm_first {
|
|
print STDERR "Timeout for Fiasco-UX hit!\n" if $Verbose;
|
|
print STDERR "Sending SIGINT to $pid\n" if $Verbose;
|
|
|
|
kill INT => $pid;
|
|
$SIG{ALRM} = \&got_sig_alarm_second;
|
|
alarm 2; # time to flush output
|
|
|
|
# return to keep on filtering the output of Fiasco
|
|
}
|
|
|
|
##
|
|
# Runs the timer and kills fiasco if runaway
|
|
#
|
|
# - sets the signal handler
|
|
# - initializes the timeout
|
|
sub set_alarm {
|
|
$SIG{ALRM} = \&got_sig_alarm_first;
|
|
alarm $ALARM_TIMEOUT;
|
|
print STDERR "Set alarm to $ALARM_TIMEOUT seconds\n" if $Verbose;
|
|
}
|
|
|
|
##
|
|
# Build the fiasco command line
|
|
#
|
|
# Adds the binaries with the appropriate parameters.
|
|
sub fiascoux_cmdline() {
|
|
(my $p = $FIASCOUX) =~ s/\/[^\/]+$//;
|
|
my $cmdline = "$FIASCOUX";
|
|
$cmdline .= " $FIASCOUX_PARAM" if defined $FIASCOUX_PARAM;
|
|
$cmdline .= " -symbols $Fiasco_Dir/Symbols" if $Use_Symbols;
|
|
$cmdline .= " -lines $Fiasco_Dir/Lines" if $Use_Lines;
|
|
$cmdline .= " -m $Memory";
|
|
$cmdline .= " -R $progs{roottask}"; # -quiet";
|
|
$cmdline .= "\"" if $Use_Symbols || $Use_Lines;
|
|
$cmdline .= " -symbols" if $Use_Symbols;
|
|
$cmdline .= " -lines" if $Use_Lines;
|
|
$cmdline .= "\"" if $Use_Symbols || $Use_Lines;
|
|
$cmdline .= " -S $progs{sigma0}"; # --quiet";
|
|
# when we change this to not cd into Fiasco dir, then
|
|
# prepend $p to $irq0
|
|
$cmdline .= " -I ".$p."/irq0";
|
|
if ($No_fb eq 1) {
|
|
$cmdline .= " -F /bin/true";
|
|
} else {
|
|
$cmdline .= " -F ".$p."/con_ux";
|
|
}
|
|
# add -l to base-servers, which already contains client and server
|
|
for my $s (@Baseservers) {
|
|
if (ref($s) ne "HASH") { # just checking for Hashes that smuggled in
|
|
$cmdline .= " -l ".$s;
|
|
}
|
|
}
|
|
$cmdline;
|
|
}
|
|
|
|
##
|
|
# filters the output of fiasco
|
|
#
|
|
# We want to see everything once roottask are finished loading.
|
|
# Therefore, we need some state machine to skip everything before and while
|
|
# roottask is running.
|
|
sub filter_fiasco_out
|
|
{
|
|
$_ = shift;
|
|
|
|
# filter escape sequences
|
|
s/[[:cntrl:]]\[(\d;)?\d{0,2}m//g;
|
|
s/
|
|
//g;
|
|
s/[[:cntrl:]]\[K//g;
|
|
s/[[:cntrl:]]\[\d+;\d+[Hfr]//g;
|
|
|
|
# check if we have to change state
|
|
if (/^Roottask: Loading \d+ modules\./ &&
|
|
$Filter_state eq $FILTER_LEVEL_FIASCO) {
|
|
$Filter_state = $FILTER_LEVEL_ROOTTASK;
|
|
print STDERR "Changed state to Roottask-Output\n" if $Verbose;
|
|
}
|
|
if ( /^$/ && $Filter_state eq $FILTER_LEVEL_ROOTTASK) {
|
|
$Filter_state = $FILTER_LEVEL_USER;
|
|
print STDERR "Changed state to Userland-Output\n" if $Verbose;
|
|
}
|
|
|
|
# skip everything before and from roottask
|
|
return undef if $Filter_state < $Filter_level;
|
|
|
|
# if we are not supposed to filter anything, then return the line
|
|
return $_ if $Filter_level eq $FILTER_LEVEL_FIASCO;
|
|
|
|
# filter empty lines
|
|
return undef if /^$/;
|
|
|
|
# filter JDB warning and prompt
|
|
return undef if /^Terminal probably too small, should be at least/;
|
|
return undef if /^\([a-f0-9]\.\d\d\) jdb:/;
|
|
return undef if /^--.*ESP:.*EIP:.*/;
|
|
|
|
# filter memory dump
|
|
return undef if /^([a-f0-9]){8}:/;
|
|
|
|
$_;
|
|
}
|
|
|
|
##
|
|
# call_test
|
|
sub run_fiasco {
|
|
my $cmdline = fiascoux_cmdline();
|
|
print "Calling: $cmdline\n" if $Verbose;
|
|
|
|
$pid = open($Input, "$cmdline|");
|
|
die "Can't start Fiasco: $!" unless defined $pid;
|
|
print "Run Fiasco-UX in $pid\n" if $Verbose;
|
|
|
|
# if in generate mode, we redirect output to EXPECTED_OUT
|
|
# otherwise we redirect stdout to the TMP_OUT file
|
|
my $filename = $Generate ? $EXPECTED_OUT : $TMP_OUT;
|
|
open($Output, ">$filename") || die "Cannot open output file $filename";
|
|
my $oldfh = select($Output); $| = 1; select($oldfh);
|
|
print STDERR "Opened $filename, now setting timer\n" if $Verbose;
|
|
|
|
# the parent sets up the timer (it will eventually call the run_diff sub)
|
|
set_alarm();
|
|
|
|
my $o;
|
|
while (<$Input>) {
|
|
# we have to strip some lines
|
|
$o = filter_fiasco_out($_);
|
|
next unless defined $o;
|
|
print $Output $o;
|
|
}
|
|
print STDERR "Fiasco terminated.\n" if $Verbose;
|
|
close $Input;
|
|
close $Output;
|
|
# when we drop out of this loop fiasco terminated
|
|
alarm 0;
|
|
|
|
# in generate mode, simply return
|
|
exit 0 if $Generate;
|
|
|
|
exit run_diff();
|
|
}
|
|
|
|
##
|
|
# Plain run of Fiasco UX
|
|
#
|
|
# Not timer, no filter, simply run UX
|
|
sub run_plain_fiasco {
|
|
my $cmdline = fiascoux_cmdline();
|
|
print "Running UX: \"$cmdline\"\n";
|
|
exec($cmdline) or die "Can't start Fiasco: $!";
|
|
exit 1;
|
|
}
|
|
|
|
|
|
# -------------------------------------------------------------
|
|
|
|
unless (GetOptions("help|h", sub { usage(); exit(0); },
|
|
"l4dir|l=s", \$L4DIR,
|
|
"builddir=s", \$OBJ_BASE,
|
|
"fiascoux|f=s", \$FIASCOUX,
|
|
"fiascoux_param|p=s", \$FIASCOUX_PARAM,
|
|
"memory|m=s", \$Memory,
|
|
"objdir|O=s", \$OBJDIR,
|
|
"verbose|v!", \$Verbose,
|
|
"quiet|q!", \$Quiet,
|
|
"generate!", \$Generate,
|
|
"expectfail!", \$Exit_code,
|
|
"client|c=s", \$Client,
|
|
"server|s=s", \$Server,
|
|
"base|b=s", \@Baseservers,
|
|
"timeout|t=s", \$ALARM_TIMEOUT,
|
|
"plainrun!", \$Plainrun,
|
|
"filterlevel=i", \$Filter_level,
|
|
"nofb!", \$No_fb,
|
|
"symbols!", \$Use_Symbols,
|
|
"lines!", \$Use_Lines,
|
|
)) {
|
|
usage();
|
|
exit(1);
|
|
}
|
|
|
|
@Baseservers = split(/,/,join(',',@Baseservers));
|
|
|
|
$L4DIR = $ENV{L4DIR} || die "Need an L4DIR set!" unless $L4DIR;
|
|
$OBJ_BASE = $ENV{OBJ_BASE} || die "Need an builddir (OBJ_BASE) set!" unless $OBJ_BASE;
|
|
$FIASCOUX = $ENV{FIASCOUX} || die "Need a Fiasco-UX path!" unless $FIASCOUX;
|
|
if ($ENV{EXPECTED_OUT}) { $EXPECTED_OUT = $ENV{EXPECTED_OUT}; }
|
|
die "Need filename of expected output!" unless $EXPECTED_OUT;
|
|
if ($ENV{TMP_OUT}) { $TMP_OUT = $ENV{TMP_OUT}; }
|
|
die "No valid temporary file set!" unless $TMP_OUT;
|
|
$PKGNAME = $ENV{PKGNAME} || $Server
|
|
|| die "No package-name set!" unless $PKGNAME;
|
|
if ($ENV{COMPARE_CMD}) { $COMPARE_CMD = $ENV{COMPARE_CMD}; }
|
|
die "No compare command set!" unless $COMPARE_CMD;
|
|
|
|
check_for_l4dir();
|
|
check_for_fiasco_ux();
|
|
check_for_userland();
|
|
|
|
##
|
|
# The package test script:
|
|
# This script forks off Fiasco and sets up a timeout. When the timeout
|
|
# strikes, it sends a SIGINT to Fiasco to force a flush of the output buffer.
|
|
# Then it sends a SIGKILL to terminate Fiasco.
|
|
print "Main called by $$.\n" if $Verbose;
|
|
|
|
# if plainrun, start run_plain_fiasco (it never returns)
|
|
run_plain_fiasco() if $Plainrun;
|
|
|
|
# this function forks off fiasco
|
|
run_fiasco();
|
|
|
|
# when we reach this point, something terribly went wrong
|
|
die "Oops, error in script!\n";
|