summaryrefslogtreecommitdiff
path: root/libdde-linux26/mk/ptest
blob: e85d53b6e43744af142ecde95061ba5fbc9eb479 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
#! /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-l4v2';
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";