]> rtime.felk.cvut.cz Git - l4.git/blob - l4/mk/ptest
Inital import
[l4.git] / l4 / mk / ptest
1 #! /usr/bin/perl -W
2 #
3 # Run package tests using Fiasco-UX
4 #
5 #
6 #  Adam Lackorzynski <adam@os.inf.tu-dresden.de>
7 #  Ronald Aigner <ra3@os.inf.tu-dresden.de>
8 #
9 # This script is derived from the autocheck script which can be found in
10 # kernel/fiasco/tools. It has been slightly modified to suit the demands of
11 # the package tests.
12 #
13
14 # make it pass for now
15 #exit 0;
16
17 use strict;
18 use Getopt::Long;
19
20 my $FIASCOUX;
21 my $FIASCOUX_PARAM;
22 my $L4DIR;
23 my $OBJ_BASE;
24 my $OBJDIR = 'OBJ-x86_586-l4f';
25 my $EXPECTED_OUT = 'expected.txt';
26 my $TMP_OUT = 'tmp_out.txt';
27 my $PKGNAME;
28 my $COMPARE_CMD = 'diff -u $EXPECTED_OUT $TMP_OUT';
29
30 my $ALARM_TIMEOUT = 60; # in seconds
31
32 my %templs = (
33   roottask => '%s/pkg/roottask/server/src/%s/roottask',
34   sigma0   => '%s/pkg/sigma0/server/src/%s/sigma0',
35 );
36
37 my $Verbose  = 0;
38 my $Quiet    = 0;
39 my $Generate = 0;
40 my $Memory   = 32;
41 my $Plainrun = 0;
42 my $No_fb    = 0;
43 my $Use_Symbols = 0;
44 my $Use_Lines = 0;
45
46 my %progs;
47 my %results;
48 my %output;
49 my @Baseservers;
50 my $Client;
51 my $Server;
52 my $Fiasco_Dir;
53
54 # filehandle for fiasco output
55 my $Input;
56 my $Output;
57
58 my $Exit_code = 0;
59 my $pid = 0;
60
61 my $FILTER_LEVEL_FIASCO = 0;
62 my $FILTER_LEVEL_ROOTTASK = 1;
63 my $FILTER_LEVEL_USER = 2;
64 my $Filter_state = $FILTER_LEVEL_FIASCO;
65 my $Filter_level = $FILTER_LEVEL_USER;
66
67 sub usage() {
68   print <<EOU;
69 $0 [options] -c <client> -s <server>
70
71  --l4dir, -l path         Path to an L4 directory
72  --fiascoux, -f file      Path to the Fiasco-UX binary
73  --fiascoux_param, -p params Parameters for Fiasco-UX
74  --memory, -m megabyte    Number of Megabytes used by Fiasco. (default: 32)
75  --objdir, -O objdir      Object dir, currently: $OBJDIR
76  --verbose, -v            Be verbose (e.g. show output of L4 apps)
77  --quiet, -q              Tell nothing, just set the exit code
78  --generate               Generate the output file instead of comparing to it
79  --server, -s file        The server to be tested
80  --client, -c file        The file to run as test-application
81  --base, -b file          Additional base servers required by the application
82                           Can be a comma seperated list. (Only specify the
83                           binary names!) (default: log, names, dm_phys)
84  --timeout, -t timeout    Time in seconds to wait before shooting down a 
85                           runaway fiasco. (default: 60)
86  --expectfail             Expect failure. Return 0 on failure, 1 on success.
87
88  Environment variables:
89   L4DIR                   Path to an L4 directory
90   OBJ_BASE                Path to build directory
91   FIASCOUX                Path to the Fiasco-UX binary
92   EXPECTED_OUT            Filename of the file containing the expected output
93   TMP_OUT                 Filename of the tempfile containing Fiasco's output
94   COMPARE_CMD             Command to compare output and expected output
95
96  Notes:
97   Environment variables override options!
98 EOU
99 }
100
101 ##
102 # Check if L4DIR looks like an L4 directory
103 sub check_for_l4dir() {
104   unless (-d "$L4DIR/pkg/l4sys/include" ||
105           -d "$L4DIR/../kernel/fiasco/src/kern/ux") {
106     die "$L4DIR doesn't look like an L4 directory!";
107   }
108 }
109
110 ##
111 # Just check if the supplied binary in $FIASCOUX is really
112 # a Fiasco UX version. Native versions will just segfault.
113 sub check_for_fiasco_ux() {
114
115   unless ((-x $FIASCOUX && -f $FIASCOUX) ||
116           (-l $FIASCOUX && -x readlink $FIASCOUX && -f readlink $FIASCOUX)) {
117     die "$FIASCOUX: Does not exist or isn't an executable file";
118   }
119   
120   system("$FIASCOUX -h >/dev/null 2>&1");
121   die "$FIASCOUX doesn't seem to be a UX version." if $?;
122
123   $FIASCOUX =~ /(.*)\/([^\/]*)$/;
124   $Fiasco_Dir = $1;
125 }
126
127 # check for user app in binary path of L4
128 sub check_user_app($)
129 {
130   my $app_with_args = shift;
131
132   # extract arch and api from OBJ-dir
133   $OBJDIR =~ /OBJ-([^-]*)-(.*)/;
134   my $arch = $1;
135   my $api = $2;
136
137   my @check = split(/\s+/, $app_with_args);
138   my $app = shift @check;
139
140   my $p = sprintf "%s/bin/%s/%s/%s", $OBJ_BASE, $arch, $api, $app;
141   die "There's no $p!" unless -f $p;
142
143   return $p.'\\ '.join('\\ ', @check) if @check;
144   $p;
145 }
146
147 ##
148 # Check for userland (roottask, sigma0, ...)
149 sub check_for_userland() {
150   foreach my $t (keys %templs) {
151     my $p = sprintf $templs{$t}, $OBJ_BASE, $OBJDIR;
152     die "There's no $p!" unless -f $p;
153     $progs{$t} = $p;
154   }
155
156   # check for base-servers
157   my @servers = @Baseservers;
158   @Baseservers = {};
159   if (scalar(@servers) eq 0) {
160     @servers = ( "log", "names", "dm_phys" );
161   }
162   
163   # iterate over servers and test if they exist in the bin dir
164   foreach my $s (@servers) {
165     push @Baseservers, check_user_app($s);
166   }
167
168   # test client and server
169   push @Baseservers, check_user_app($Client) if defined($Client);
170   push @Baseservers, check_user_app($Server);
171 }
172
173 ##
174 # Does the comparison of the output
175 sub run_diff {
176   print STDERR "Running compare command \"$COMPARE_CMD\".\n" if $Verbose;
177   open(TMP, "$COMPARE_CMD |") or die "Cannot run diff: $!";
178   my @diff = <TMP>;
179   close TMP;
180   
181   # reset console in interactive mode
182   system "if tty -s; then stty echo icrnl icanon; fi";
183   
184   # check if there are differences between expected and generated output
185   if (@diff == 0) {
186     print STDERR "Output of test in $PKGNAME ok.\n";
187     return $Exit_code;
188   }
189
190   print STDERR "Test in $PKGNAME generated unexpected output:\n";
191   @diff = splice @diff, 1000 if @diff > 1000;
192   print STDERR @diff, "\n";
193   return 1 - $Exit_code;
194 }
195
196 ##
197 # Called if second alarm signal received
198 #
199 # Now all the output of Fiasco-UX should be through and we can savely kill
200 # Fiasco-UX. We then check the generated output and terminate ourselves.
201 sub got_sig_alarm_second {
202   print STDERR "Timeout for flushing Fiasco-UX output\n" if $Verbose;
203   print STDERR "Sending SIGKILL to $pid and diff output\n" if $Verbose;
204
205   kill KILL => $pid;
206   alarm 0;
207
208   # some sanity checks
209   if ( $Filter_state < $FILTER_LEVEL_USER ) {
210     print $Output "\n";
211     print $Output "User tasks did not start. Maybe overlapping modules?\n";
212     print $Output "Run \"" . fiascoux_cmdline() . "\" manually to check.\n";
213   }
214   close $Output;
215
216   # in generate mode, simply return
217   if ($Generate) {
218     system "stty echo";
219     exit 0;
220   }
221   
222   exit run_diff();
223 }
224
225 ##
226 # Called if first alarm signal received
227 #
228 # To flush Fiasco Output we send it a SIGINT (^C). Then we set up a second
229 # timeout and return, so the filter can process the output which we forced to
230 # be flushed.
231 sub got_sig_alarm_first {
232   print STDERR "Timeout for Fiasco-UX hit!\n" if $Verbose;
233   print STDERR "Sending SIGINT to $pid\n" if $Verbose;
234   
235   kill INT => $pid;
236   $SIG{ALRM} = \&got_sig_alarm_second;
237   alarm 2; # time to flush output
238
239   # return to keep on filtering the output of Fiasco
240 }
241
242 ##
243 # Runs the timer and kills fiasco if runaway
244 #
245 # - sets the signal handler
246 # - initializes the timeout
247 sub set_alarm {
248   $SIG{ALRM} = \&got_sig_alarm_first;
249   alarm $ALARM_TIMEOUT;
250   print STDERR "Set alarm to $ALARM_TIMEOUT seconds\n" if $Verbose;
251 }
252
253 ##
254 # Build the fiasco command line
255 #
256 # Adds the binaries with the appropriate parameters.
257 sub fiascoux_cmdline() {
258   (my $p = $FIASCOUX) =~ s/\/[^\/]+$//;
259   my $cmdline = "$FIASCOUX";
260   $cmdline .= " $FIASCOUX_PARAM" if defined $FIASCOUX_PARAM;
261   $cmdline .= " -symbols $Fiasco_Dir/Symbols" if $Use_Symbols;
262   $cmdline .= " -lines $Fiasco_Dir/Lines" if $Use_Lines;
263   $cmdline .= " -m $Memory";
264   $cmdline .= " -R $progs{roottask}"; # -quiet";
265   $cmdline .= "\"" if $Use_Symbols || $Use_Lines;
266   $cmdline .= " -symbols" if $Use_Symbols;
267   $cmdline .= " -lines" if $Use_Lines;
268   $cmdline .= "\"" if $Use_Symbols || $Use_Lines;
269   $cmdline .= " -S $progs{sigma0}"; # --quiet";
270   # when we change this to not cd into Fiasco dir, then
271   # prepend $p to $irq0
272   $cmdline .= " -I ".$p."/irq0";
273   if ($No_fb eq 1) {
274     $cmdline .= " -F /bin/true";
275   } else {
276     $cmdline .= " -F ".$p."/con_ux";
277   }
278   # add -l to base-servers, which already contains client and server
279   for my $s (@Baseservers) {
280     if (ref($s) ne "HASH") { # just checking for Hashes that smuggled in
281       $cmdline .= " -l ".$s;
282     }
283   }
284   $cmdline;
285 }
286
287 ##
288 # filters the output of fiasco
289 #
290 # We want to see everything once roottask are finished loading.
291 # Therefore, we need some state machine to skip everything before and while
292 # roottask is running.
293 sub filter_fiasco_out
294 {
295   $_ = shift;
296
297   # filter escape sequences
298   s/[[:cntrl:]]\[(\d;)?\d{0,2}m//g;
299   s/\r//g;
300   s/[[:cntrl:]]\[K//g;
301   s/[[:cntrl:]]\[\d+;\d+[Hfr]//g;
302
303   # check if we have to change state
304   if (/^Roottask: Loading \d+ modules\./ &&
305       $Filter_state eq $FILTER_LEVEL_FIASCO) {
306     $Filter_state = $FILTER_LEVEL_ROOTTASK;
307     print STDERR "Changed state to Roottask-Output\n" if $Verbose;
308   }
309   if ( /^$/ && $Filter_state eq $FILTER_LEVEL_ROOTTASK) {
310     $Filter_state = $FILTER_LEVEL_USER;
311     print STDERR "Changed state to Userland-Output\n" if $Verbose;
312   }
313
314   # skip everything before and from roottask
315   return undef if $Filter_state < $Filter_level;
316
317   # if we are not supposed to filter anything, then return the line
318   return $_ if $Filter_level eq $FILTER_LEVEL_FIASCO;
319
320   # filter empty lines
321   return undef if /^$/;
322
323   # filter JDB warning and prompt
324   return undef if /^Terminal probably too small, should be at least/;
325   return undef if /^\([a-f0-9]\.\d\d\) jdb:/;
326   return undef if /^--.*ESP:.*EIP:.*/;
327
328   # filter memory dump
329   return undef if /^([a-f0-9]){8}:/;
330   
331   $_;
332 }
333
334 ##
335 # call_test
336 sub run_fiasco {
337   my $cmdline = fiascoux_cmdline();
338   print "Calling: $cmdline\n" if $Verbose;
339
340   $pid = open($Input, "$cmdline|");
341   die "Can't start Fiasco: $!" unless defined $pid;
342   print "Run Fiasco-UX in $pid\n" if $Verbose;
343   
344   # if in generate mode, we redirect output to EXPECTED_OUT
345   # otherwise we redirect stdout to the TMP_OUT file
346   my $filename = $Generate ? $EXPECTED_OUT : $TMP_OUT;
347   open($Output, ">$filename") || die "Cannot open output file $filename";
348   my $oldfh = select($Output); $| = 1; select($oldfh);
349   print STDERR "Opened $filename, now setting timer\n" if $Verbose;
350
351   # the parent sets up the timer (it will eventually call the run_diff sub)
352   set_alarm();
353
354   my $o;
355   while (<$Input>) {
356     # we have to strip some lines
357     $o = filter_fiasco_out($_);
358     next unless defined $o;
359     print $Output $o;
360   }
361   print STDERR "Fiasco terminated.\n" if $Verbose;
362   close $Input;
363   close $Output;
364   # when we drop out of this loop fiasco terminated
365   alarm 0;
366
367   # in generate mode, simply return
368   exit 0 if $Generate;
369   
370   exit run_diff();
371 }
372
373 ##
374 # Plain run of Fiasco UX
375 #
376 # Not timer, no filter, simply run UX
377 sub run_plain_fiasco {
378   my $cmdline = fiascoux_cmdline();
379   print "Running UX: \"$cmdline\"\n";
380   exec($cmdline) or die "Can't start Fiasco: $!";
381   exit 1;
382 }
383  
384
385 # -------------------------------------------------------------
386
387 unless (GetOptions("help|h", sub { usage(); exit(0); },
388                    "l4dir|l=s", \$L4DIR,
389                    "builddir=s", \$OBJ_BASE,
390                    "fiascoux|f=s", \$FIASCOUX,
391                    "fiascoux_param|p=s", \$FIASCOUX_PARAM,
392                    "memory|m=s", \$Memory,
393                    "objdir|O=s", \$OBJDIR,
394                    "verbose|v!", \$Verbose,
395                    "quiet|q!", \$Quiet,
396                    "generate!", \$Generate,
397                    "expectfail!", \$Exit_code,
398                    "client|c=s", \$Client,
399                    "server|s=s", \$Server,
400                    "base|b=s", \@Baseservers,
401                    "timeout|t=s", \$ALARM_TIMEOUT,
402                    "plainrun!", \$Plainrun,
403                    "filterlevel=i", \$Filter_level,
404                    "nofb!", \$No_fb,
405                    "symbols!", \$Use_Symbols,
406                    "lines!", \$Use_Lines,
407                    )) {
408   usage();
409   exit(1);
410 }
411
412 @Baseservers = split(/,/,join(',',@Baseservers));
413
414 $L4DIR = $ENV{L4DIR}       || die "Need an L4DIR set!" unless $L4DIR;
415 $OBJ_BASE = $ENV{OBJ_BASE} || die "Need an builddir (OBJ_BASE) set!" unless $OBJ_BASE;
416 $FIASCOUX = $ENV{FIASCOUX} || die "Need a Fiasco-UX path!" unless $FIASCOUX;
417 if ($ENV{EXPECTED_OUT}) { $EXPECTED_OUT = $ENV{EXPECTED_OUT}; }
418 die "Need filename of expected output!" unless $EXPECTED_OUT;
419 if ($ENV{TMP_OUT}) { $TMP_OUT = $ENV{TMP_OUT}; }
420 die "No valid temporary file set!" unless $TMP_OUT;
421 $PKGNAME = $ENV{PKGNAME} || $Server 
422         || die "No package-name set!" unless $PKGNAME;
423 if ($ENV{COMPARE_CMD}) { $COMPARE_CMD = $ENV{COMPARE_CMD}; }
424 die "No compare command set!" unless $COMPARE_CMD;
425
426 check_for_l4dir();
427 check_for_fiasco_ux();
428 check_for_userland();
429
430 ##
431 # The package test script:
432 # This script forks off Fiasco and sets up a timeout. When the timeout
433 # strikes, it sends a SIGINT to Fiasco to force a flush of the output buffer.
434 # Then it sends a SIGKILL to terminate Fiasco.
435 print "Main called by $$.\n" if $Verbose;
436
437 # if plainrun, start run_plain_fiasco (it never returns)
438 run_plain_fiasco() if $Plainrun;
439
440 # this function forks off fiasco
441 run_fiasco();
442
443 # when we reach this point, something terribly went wrong
444 die "Oops, error in script!\n";