OpenCores
URL https://opencores.org/ocsvn/w11/w11/trunk

Subversion Repositories w11

[/] [w11/] [tags/] [w11a_V0.74/] [tools/] [bin/] [tbrun] - Blame information for rev 38

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 37 wfjm
#!/usr/bin/perl -w
2
# $Id: tbrun 808 2016-09-17 13:02:46Z mueller $
3
#
4
# Copyright 2016- by Walter F.J. Mueller 
5
#
6
# This program is free software; you may redistribute and/or modify it under
7
# the terms of the GNU General Public License as published by the Free
8
# Software Foundation, either version 2, or at your option any later version.
9
#
10
# This program is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
12
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13
# for complete details.
14
#
15
#  Revision History:
16
# Date         Rev Version  Comment
17
# 2016-09-17   808   1.0    Initial version
18
# 2016-08-09   796   0.1    First draft
19
#
20
 
21
use 5.14.0;                                 # require Perl 5.14 or higher
22
use strict;                                 # require strict checking
23
 
24
use Getopt::Long;
25
use FileHandle;
26
use YAML::XS;
27
use Cwd;
28
use IO::Select;
29
use Time::HiRes qw(gettimeofday);
30
 
31
my %opts = ();
32
 
33
GetOptions(\%opts, "tag=s@", "exclude=s@", "mode=s",
34
           "jobs=i", "tee=s", "tmax=i", "dry", "trace",
35
           "nomake", "norun",
36
           "rlmon", "rbmon", "bwait=i", "swait=i"
37
          )
38
  or die "bad options";
39
 
40
sub setup_tagfilter;
41
sub check_tagfilter;
42
sub check_modefilter;
43
sub include_file;
44
sub read_file;
45
sub load_yaml;
46
sub check_keys;
47
sub expand_vars;
48
sub merge_lines;
49
sub merge_expand;
50
sub key_or_def;
51
sub handle_include;
52
sub handle_default;
53
sub handle_itest;
54
sub tpr;
55
sub tpre;
56
sub print_trace;
57
sub run_tests_single;
58
sub run_tests_multi;
59
 
60
my @tlist;
61
my @olist;
62
my @wlist;
63
 
64
 
65
my %keys_include = ( include => { mode => 'm', ref => ''},
66
                     tag     => { mode => 'o', ref => 'ARRAY'}
67
                   );
68
my %keys_default = ( default => { mode => 'm', ref => 'HASH'}
69
                   );
70
my %keys_defhash = ( tag     => { mode => 'o', ref => 'ARRAY'},
71
                     mode    => { mode => 'o', ref => ''}
72
                   );
73
my %keys_itest   = ( test    => { mode => 'm', ref => ''},
74
                     tag     => { mode => 'o', ref => 'ARRAY'},
75
                     mode    => { mode => 'o', ref => ''}
76
                   );
77
 
78
my $nseen = 0;
79
my $ntest = 0;
80
my $ndone = 0;
81
my $nfail = 0;
82
my $inicwd = getcwd();
83
my %gblvars;
84
 
85
$gblvars{ise_modes} = '[bsft]sim,ISim_[bsft]sim';
86
$gblvars{ise_modes_noisim} = '[bsft]sim';             # when ISim not possible
87
$gblvars{ise_modes_nossim} = 'bsim,ISim_bsim';        # when ssim not available
88
#
89
$gblvars{viv_modes} = '[bsor]sim,XSim_[bsorept]sim';
90
$gblvars{viv_modes_nossim} = 'bsim,XSim_bsim';        # when ssim not available
91
 
92
autoflush STDOUT 1 if -p STDOUT || -t STDOUT;
93
my $ticker_on = -t STDOUT;
94
 
95
my $fh_tee;
96
if (defined $opts{tee} && $opts{tee} ne '') {
97
  $fh_tee = new FileHandle;
98
  $fh_tee->open($opts{tee},'>') or die "failed to open for write '$opts{tee}'";
99
}
100
 
101
$opts{tag}  = ['default'] unless defined $opts{tag};
102
$opts{mode} = 'bsim'      unless defined $opts{mode};
103
 
104
my %modecache;
105
my @modelist;
106
foreach (split /,/,$opts{mode}) {
107
  $_ .= '_bsim' if m/^[IX]Sim$/;
108
  push @modelist, $_;
109
}
110
 
111
push @ARGV, 'tbrun.yml' unless scalar( @ARGV);
112
 
113
my @tagincl = setup_tagfilter($opts{tag});
114
my @tagexcl = setup_tagfilter($opts{exclude});
115
 
116
foreach my $fnam (@ARGV) {
117
  include_file($fnam);
118
}
119
 
120
$ntest = scalar(@tlist);
121
unless ($ntest) {
122
  tpre(sprintf "tbrun-E: %d tests found, none selected\n", $nseen);
123
  exit 2;
124
}
125
 
126
if (defined $opts{jobs}) {
127
  run_tests_multi();
128
} else {
129
  run_tests_single();
130
}
131
 
132
if (defined $opts{dry}) {
133
  tpr(sprintf "#tbrun-I: %d tests found, %d selected\n", $nseen,$ntest);
134
}
135
 
136
if ($nfail) {
137
  tpr(sprintf "tbrun-I: %d tests failed of %d tests executed\n",$nfail,$ndone);
138
}
139
 
140
exit $nfail ? 1 : 0;
141
 
142
#-------------------------------------------------------------------------------
143
sub setup_tagfilter {
144
  my ($targlist) = @_;
145
  return () unless defined $targlist;
146
  my @tagfiltlist;
147
  foreach my $targ (@$targlist) {
148
    my @tagfilt = map { "^($_)\$" } split /,/, $targ;
149
    push @tagfiltlist, \@tagfilt;
150
  }
151
  return @tagfiltlist;
152
}
153
 
154
#-------------------------------------------------------------------------------
155
sub check_tagfilter {
156
  my ($tfiltlist,$tlist) = @_;
157
  foreach my $tfilt (@$tfiltlist) {         # loop over filters
158
    my $fok = 1;
159
    foreach my $tfele (@$tfilt) {           # loop over filter elements
160
      my $match = 0;
161
      foreach my $tag (@$tlist) {           # loop over tags
162
        $match = $tag =~ m/$tfele/;         # tag matchs filter element
163
        last if $match;
164
      }
165
      $fok = 0 unless $match;               # filter missed if one element missed
166
    }
167
    return 1 if $fok;                       # return ok of one filter matched
168
  }
169
  return 0;                                 # here if no filter matched
170
}
171
 
172
#-------------------------------------------------------------------------------
173
sub check_modefilter {
174
  my ($mode,$mlist) = @_;
175
  unless (exists $modecache{$mlist}) {
176
    my %mh;
177
    foreach my $mi (split /,/,$mlist) {
178
      if ($mi =~ m/^(.*)\[([a-z]+)\](.*)$/) {
179
        foreach (split //,$2) {
180
          $mh{$1.$_.$3} = 1;
181
        }
182
      } else {
183
        $mh{$mi} = 1;
184
      }
185
    }
186
    $modecache{$mlist} = \%mh;
187
  }
188
 
189
  my $rmh = $modecache{$mlist};
190
  return exists $$rmh{$mode};
191
}
192
 
193
#-------------------------------------------------------------------------------
194
sub include_file {
195
  my ($fnam) = @_;
196
  my $fdat = read_file($fnam);
197
  exit 2 unless defined $fdat;
198
  my $ylst = load_yaml($fdat, $fnam);
199
  exit 2 unless defined $ylst;
200
 
201
  my $oldcwd = getcwd();
202
 
203
  if ($fnam =~ m|^(.*)/(.*)$|) {
204
    chdir $1 or die "chdir to '$1' failed with '$!'";
205
  }
206
 
207
  my %defhash;
208
  foreach my $yele (@$ylst) {
209
    if (exists $yele->{include}) {
210
      handle_include($yele);
211
    } elsif (exists $yele->{default}) {
212
      handle_default($yele, \%defhash);
213
    } elsif (exists $yele->{test}) {
214
      handle_itest($yele, \%defhash);
215
    } else {
216
      tpr(sprintf "tbrun-E: unknown list element in '%s'\n  found keys: %s\n",
217
            $fnam, join(',',sort keys %$yele));
218
      exit 2;
219
    }
220
  }
221
 
222
  chdir $oldcwd or die "chdir to '$oldcwd' failed with '$!'";
223
  return;
224
}
225
 
226
#-------------------------------------------------------------------------------
227
sub read_file {
228
  my ($fnam) = @_;
229
  my $fh = new FileHandle;
230
  if (not open $fh, '<', $fnam) {
231
    my $err = $!;
232
    tpre(sprintf "tbrun-E: failed to open '%s'\n  cwd: %s\n  error: %s\n",
233
           $fnam, getcwd(), $err);
234
    return undef;
235
  }
236
  # nice trick to slurp the whole file into a variable
237
  my $fdat = do {
238
    local $/ = undef;
239
    <$fh>;
240
  };
241
  close $fh;
242
  return $fdat;
243
}
244
 
245
#-------------------------------------------------------------------------------
246
sub load_yaml {
247
  my ($fdat,$fnam) = @_;
248
  my $ylst;
249
  eval { $ylst = YAML::XS::Load($fdat); };
250
  if ($@ ne '') {
251
    my $err = $@;
252
    tpre(sprintf "tbrun-E: failed to yaml load '%s'\n  cwd: %s\n  error: %s\n",
253
           $fnam, getcwd(), $err);
254
    return undef;
255
  }
256
  if (ref $ylst ne 'ARRAY') {
257
    tpre(sprintf "tbrun-E: top level yaml is not a list but '%s'\n", ref $ylst);
258
    return undef;
259
  }
260
  foreach my $yele (@$ylst) {
261
    if (ref $yele ne 'HASH') {
262
      tpre(sprintf "tbrun-E: second level yaml is not a hash '%s'\n", ref $yele);
263
      return undef;
264
    }
265
  }
266
  return $ylst;
267
}
268
 
269
#-------------------------------------------------------------------------------
270
sub check_keys {
271
  my ($yele, $href) = @_;
272
  foreach my $keyele ( keys %$yele ) {
273
    if (not exists $href->{$keyele}) {
274
      tpre(sprintf "tbrun-E: unexpected key '%s'\n", $keyele);
275
      return 0;
276
    }
277
    my $ref = ref $yele->{$keyele};
278
    if ($ref ne $href->{$keyele}->{ref}) {
279
      tpre(sprintf "tbrun-E: key '%s' is type'%s', expected '%s'\n",
280
             $keyele, $ref, $href->{$keyele}->{ref});
281
      return 0;
282
    }
283
  }
284
  foreach my $keyref ( keys %$href ) {
285
    next if $href->{$keyref}->{mode} eq 'o';
286
    if (not exists $yele->{$keyref}) {
287
      tpre(sprintf "tbrun-E: key '%s' missing\n", $keyref);
288
      return 0;
289
    }
290
  }
291
  return 1;
292
}
293
 
294
#-------------------------------------------------------------------------------
295
sub lookup_var {
296
  my ($vnam,$hrefs) = @_;
297
  return $gblvars{$vnam} if exists $gblvars{$vnam};
298
  if ($vnam =~ m/[A-Z][A-Z0-9_]*/) {
299
    return $ENV{$vnam} if exists $ENV{$vnam};
300
  }
301
  tpre(sprintf "tbrun-E: can't replace '$vnam'\n");
302
  exit 2;
303
}
304
 
305
#-------------------------------------------------------------------------------
306
sub expand_vars {
307
  my ($txt,$hrefs) = @_;
308
  my $res = '';
309
  while ($txt ne '') {
310
    if ($txt =~ m/\$\{([a-zA-Z][a-zA-Z0-9_]*)\}/) {
311
      my $vnam = $1;
312
      my $vrep = lookup_var($vnam, $hrefs);
313
      $res .= $`;
314
      $res .= $vrep;
315
      $txt  = $';
316
    } else {
317
      $res .= $txt;
318
      last;
319
    }
320
  }
321
  return $res;
322
}
323
 
324
#-------------------------------------------------------------------------------
325
sub merge_lines {
326
  my ($txt) = @_;
327
  $txt =~ s|\s*\\\n\s*| |mg;
328
  chomp $txt;
329
  return $txt;
330
}
331
 
332
#-------------------------------------------------------------------------------
333
sub merge_expand {
334
  my ($txt,$hrefs) = @_;
335
  return expand_vars(merge_lines($txt), $hrefs);
336
}
337
 
338
#-------------------------------------------------------------------------------
339
sub key_or_def {
340
  my ($tag,$yele,$defhash) = @_;
341
  return $yele->{$tag}    if exists $yele->{$tag};
342
  return $defhash->{$tag} if exists $defhash->{$tag};
343
  return undef;
344
}
345
 
346
#-------------------------------------------------------------------------------
347
sub handle_include {
348
  my ($yele) = @_;
349
  check_keys($yele, \%keys_include) or exit 2;
350
 
351
  my $fnam = merge_expand($yele->{include}, undef);
352
  include_file($fnam);
353
 
354
  return;
355
}
356
 
357
#-------------------------------------------------------------------------------
358
sub handle_default {
359
  my ($yele, $defhash) = @_;
360
  check_keys($yele, \%keys_default) or exit 2;
361
  check_keys($yele->{default}, \%keys_defhash) or exit 2;
362
  foreach my $key (keys %{$yele->{default}}) {
363
    $$defhash{$key} = $$yele{default}{$key};
364
  }
365
  return;
366
}
367
 
368
#-------------------------------------------------------------------------------
369
sub handle_itest {
370
  my ($yele, $defhash) = @_;
371
  check_keys($yele, \%keys_itest) or exit 2;
372
 
373
  $nseen += 1;
374
 
375
  my $tlist = key_or_def('tag', $yele, $defhash);
376
  if (defined $tlist) {
377
    return unless check_tagfilter(\@tagincl, $tlist);
378
    return     if check_tagfilter(\@tagexcl, $tlist);
379
  }
380
 
381
  my $mlist = merge_expand(key_or_def('mode', $yele, $defhash), undef);
382
 
383
  foreach my $mode (@modelist) {
384
    next unless check_modefilter($mode, $mlist);
385
 
386
    my $ms = '_' . $mode;
387
    $ms =~ s/_bsim$//;
388
    $gblvars{ms} = $ms;
389
 
390
    my $test = merge_expand($yele->{test}, undef);
391
 
392
    # forward options for tbrun_tbw or tbrun_tbwrri commands
393
    if ($test =~ m/^\s*(tbrun_tbw|tbrun_tbwrri)\s+(.*)$/) {
394
      my $cmd  = $1;
395
      my $rest = $2;
396
      $test  = $cmd;
397
      $test .= ' --nomake' if $opts{nomake};
398
      $test .= ' --norun'  if $opts{norun};
399
      if ($cmd eq 'tbrun_tbwrri') {
400
        $test .= ' --rlmon' if $opts{rlmon};
401
        $test .= ' --rbmon' if $opts{rbmon};
402
        $test .= ' --bwait '.$opts{bwait} if $opts{bwait};
403
        $test .= ' --swait '.$opts{swait} if $opts{swait};
404
      }
405
      $test .= ' ' . $rest;
406
    }
407
 
408
    my $tid  = scalar(@tlist);
409
    my $tmsg = sprintf "t%03d - tags: ", $tid;
410
    $tmsg .= join ',',@$tlist if defined $tlist;
411
 
412
    my %titem;
413
    $titem{id}   = $tid;
414
    $titem{cd}   = getcwd();
415
    $titem{test} = $test;
416
    $titem{tag}  = $tlist;
417
    $titem{tmsg} = $tmsg;
418
 
419
    push @{$titem{locks}}, $titem{cd};
420
 
421
    push @tlist, \%titem;
422
 
423
    delete $gblvars{ms};
424
  }
425
 
426
  return;
427
}
428
 
429
#-------------------------------------------------------------------------------
430
sub tpr {
431
  my ($txt) = @_;
432
  print $txt;
433
  print $fh_tee $txt if defined $fh_tee;
434
  return;
435
}
436
 
437
#-------------------------------------------------------------------------------
438
sub tpre {
439
  my ($txt) = @_;
440
  print STDERR $txt;
441
  print $fh_tee $txt if defined $fh_tee;
442
  return;
443
}
444
 
445
#-------------------------------------------------------------------------------
446
sub max {
447
  my ($a,$b) = @_;
448
  return ($a > $b) ? $a : $b;
449
}
450
 
451
#-------------------------------------------------------------------------------
452
sub open_job_fh {
453
  my ($cmd) = @_;
454
  my $fh = new FileHandle;
455
 
456
  # add STDERR->STDOUT redirect (create sub shell of needed)
457
  $cmd = '(' . $cmd . ')' if  $cmd =~ m/\n/g;
458
  $cmd .= ' 2>&1';
459
 
460
  # open returns pid of created process in case an in or out pipe is created
461
  my $pid = open $fh, '-|', $cmd;
462
  # print "+++1 $pid\n";
463
 
464
  if (not $pid) {
465
    my $err = $!;
466
    my $msg = sprintf "tbrun-E: failed to start '%s'\n  cwd: %s\n  error: %s\n",
467
                $cmd, getcwd(), $err;
468
    return (undef, undef, $msg);
469
  }
470
  return ($fh, $pid, undef);
471
}
472
 
473
#-------------------------------------------------------------------------------
474
sub run_tests_single {
475
  my $drycd = '';
476
  foreach my $titem (@tlist) {
477
    my $cdir = $titem->{cd};
478
    my $test = $titem->{test};
479
 
480
    chdir $inicwd or die "chdir to '$inicwd' failed with '$!'";
481
 
482
    if ($opts{dry}) {
483
      if ($cdir ne $drycd) {
484
        tpr("#------------------------------------------------------------\n");
485
        tpr("cd $cdir\n");
486
        $drycd = $cdir;
487
      }
488
      tpr("#----------------------------------------\n");
489
      tpr("# $titem->{tmsg}\n");
490
      tpr("$test\n");
491
 
492
    } else {
493
      tpr("#----------------------------------------\n");
494
      tpr("# $titem->{tmsg}\n");
495
      $ndone += 1;
496
      my $cmd = '';
497
      $cmd .= "cd $cdir";
498
      $cmd .= "\n";
499
      $cmd .= "$test";
500
 
501
      my ($fh,$pid,$msg) = open_job_fh($cmd);
502
      if (not defined $fh) {
503
        tpre($msg);
504
      } else {
505
        while (<$fh>) {
506
          print $_;
507
        }
508
        if (not close $fh) {
509
          my $err = $?;
510
          tpr(sprintf "tbrun-I: test FAILed with exit status %d,%d\n",
511
                ($err>>8), ($err&0xff));
512
          $nfail += 1;
513
        }
514
      }
515
    }
516
  }
517
 
518
  if ($opts{dry}) {
519
    tpr("#------------------------------------------------------------\n");
520
    tpr(sprintf "cd %s\n", $inicwd);
521
  }
522
 
523
  return;
524
}
525
 
526
#-------------------------------------------------------------------------------
527
sub print_ticker {
528
  return unless $ticker_on;
529
 
530
  my ($rwlist) = @_;
531
  my $msg = '';
532
  state $lastlength = 0;
533
 
534
  if (defined $rwlist) {
535
    my $time_now = gettimeofday();
536
    $msg = '#-I: ' . join '; ', map {
537
      sprintf('t%03d: %dl %3.1fs',
538
              $_->{id}, $_->{nlines}, $time_now-$_->{tstart})
539
      } @$rwlist;
540
    $msg = substr($msg,0,75) . ' ...' if  length($msg)  >79;
541
    unless (defined $opts{trace}) {
542
      my $suff = sprintf '(%dt,%dw,%do)',
543
                 scalar(@tlist), scalar(@wlist), scalar(@olist);
544
      if (length($suff) + length($msg) + 1 <= 79) {
545
        $msg .= ' ' . $suff;
546
      } else {
547
        $msg  = substr($msg,0,79-6-length($suff)) . ' ... ' . $suff;
548
      }
549
    }
550
  }
551
  my $newlength = length($msg);
552
  $msg .= ' ' x ($lastlength - $newlength) if $lastlength > $newlength;
553
  print $msg . "\r";
554
  $lastlength = $newlength;
555
  return;
556
}
557
 
558
#-------------------------------------------------------------------------------
559
sub print_jobs {
560
  while (defined $olist[0]->{exitcode}) {
561
    print_ticker();
562
    my $titem = shift @olist;
563
    tpr("#----------------------------------------\n");
564
    tpr("# $titem->{tmsg}\n");
565
    tpr($titem->{out});
566
  }
567
  return;
568
}
569
 
570
#-------------------------------------------------------------------------------
571
sub print_trace {
572
  my ($titem) = @_;
573
  my $pref = '';
574
  my $suff = sprintf '(%dt,%dw,%do)',
575
               scalar(@tlist), scalar(@wlist), scalar(@olist);
576
  if (defined $titem->{exitcode}) {
577
    $pref = ($titem->{exitcode}==0) ? 'pass ' : 'FAIL ';
578
  } else {
579
    $pref = 'start';
580
  }
581
  my $txt = '#-I: ' . $pref . ' ' . $titem->{tmsg};
582
  $txt .= ' ' . $suff;
583
  $txt .= "\n";
584
  print_ticker();
585
  tpr($txt);
586
  return;
587
}
588
 
589
#-------------------------------------------------------------------------------
590
sub start_jobs {
591
 
592
  # initialize lock hash
593
  my %locks;
594
  foreach my $titem (@wlist) {
595
    foreach my $lock (@{$titem->{locks}}) {
596
      $locks{$lock} = 1;
597
    }
598
  }
599
 
600
  # look for suitable tasks
601
  for (my $i=0; $i < scalar(@tlist) && scalar(@wlist) < $opts{jobs}; ) {
602
    my $titem = $tlist[$i];
603
    my $nlock = 0;
604
    foreach my $lock (@{$titem->{locks}}) {
605
      if ($locks{$lock}) {
606
        $nlock += 1;
607
        last;
608
      }
609
    }
610
 
611
    # suitable task found
612
    if ($nlock == 0) {
613
      my $cdir = $titem->{cd};
614
      my $test = $titem->{test};
615
      $ndone += 1;
616
 
617
      my $cmd = '';
618
      if ($opts{dry}) {
619
        $cmd .= "cd $cdir";
620
        $cmd .= "\n";
621
        $cmd .= "perl -e 'select(undef, undef, undef, 0.2+1.6*rand( 1.))'";
622
        $cmd .= "\n";
623
        $cmd .= "echo \"cd $cdir\"";
624
        $cmd .= "\n";
625
        $cmd .= "echo \"$test\"";
626
      } else {
627
        $cmd .= "cd $cdir";
628
        $cmd .= "\n";
629
        $cmd .= "$test";
630
      }
631
 
632
      # start job
633
      my ($fh,$pid,$msg) = open_job_fh($cmd);
634
      if (not defined $fh) {
635
        $titem->{out} = $msg;
636
        $titem->{exitcode} = 1;
637
        print_trace($titem) if $opts{trace};
638
        print_jobs();
639
      } else {
640
        $titem->{fh}     = $fh;
641
        $titem->{fd}     = fileno($fh);
642
        $titem->{pid}    = $pid;
643
        $titem->{out}    = '';
644
        $titem->{tstart} = gettimeofday();
645
        $titem->{nlines} = 0;
646
        push @wlist, $titem;
647
        foreach my $lock (@{$titem->{locks}}) {
648
          $locks{$lock} = 1;
649
        }
650
        print_trace($titem) if $opts{trace};
651
      }
652
      splice @tlist, $i, 1;                   # remove from tlist
653
      next;                                   # and re-test i'th list element
654
    } # if ($nlock == 0)
655
 
656
    $i += 1;                                # inspect nexyt list element
657
  } # for (my $i=0; ...
658
  return;
659
}
660
 
661
#-------------------------------------------------------------------------------
662
sub kill_job {
663
  my ($titem, $trun) = @_;
664
  my $pid  = $titem->{pid};
665
  my $pgid = getpgrp(0);
666
  my %phash;
667
 
668
  $titem->{killed} = $trun;
669
 
670
  # get process tree data (for whole user, no pgid filtering possible
671
  my $rank = 0;
672
  open PS,"ps -H -o pid,ppid,pgid,comm --user $ENV{USER}|";
673
  while () {
674
    chomp;
675
    next unless m/^\s*(\d+)\s+(\d+)\s+(\d+)\s(.*)$/;
676
    my $cpid  = $1;
677
    my $cppid = $2;
678
    my $cpgid = $3;
679
    my $cargs = $4;
680
    next unless $cpgid == $pgid;            # only current process group
681
    next if $cargs =~ m/^\s*ps\s*$/;        # skip the 'ps' process itself
682
    $phash{$cpid}->{ppid} = $cppid;
683
    $phash{$cpid}->{pgid} = $cpgid;
684
    $phash{$cpid}->{args} = $cargs;
685
    $phash{$cpid}->{rank} = $rank++;
686
    push @{$phash{$cppid}->{childs}}, $cpid;
687
  }
688
  close PS;
689
 
690
  # sanity check 1: own tbrun process should be included
691
  unless (exists $phash{$$}) {
692
    print_ticker();
693
    printf "-E: tmax kill logic error: tbrun master pid not in phash\n";
694
    return;
695
  }
696
  # sanity check 2: job to be killed should be child of master tbrun
697
  unless ($phash{$pid}->{ppid} == $$) {
698
    print_ticker();
699
    printf "-E: tmax kill logic error: job not child of tbrun\n";
700
    return;
701
  }
702
 
703
  # determine number of leading blanks in master tbrun line
704
  my $nstrip = 0;
705
  $nstrip = length($1) if ($phash{$$}->{args} =~ m/^(\s*)/);
706
 
707
  # recursively mark all childs of job master
708
  my @pids = ($pid);
709
  while (scalar(@pids)) {
710
    my $cpid = shift @pids;
711
    if (not exists $phash{$cpid}) {
712
      print_ticker();
713
      printf "-E: tmax kill logic error: child pid not in phash\n";
714
      return;
715
    }
716
    $phash{$cpid}->{kill} = 1;
717
    if (exists $phash{$cpid}->{childs}) {
718
      push @pids, @{$phash{$cpid}->{childs}};
719
    }
720
  }
721
 
722
  # build list of pid to be killed, and trace message
723
  my @kpids;
724
  my @ktext;
725
  foreach my $cpid (sort {$phash{$a}->{rank} <=> $phash{$b}->{rank} }
726
                    grep {$phash{$_}->{kill}}
727
                    keys %phash) {
728
    push @kpids, $cpid;
729
    push @ktext, sprintf "#    %6d %6d %6d %s",
730
      $cpid, $phash{$cpid}->{ppid},
731
        $phash{$cpid}->{pgid},
732
        substr($phash{$cpid}->{args}, $nstrip);
733
  }
734
 
735
  # print trace message, if selected
736
  if ($opts{trace}) {
737
    print_ticker();
738
    printf "#-I: kill  t%03d after %3.1fs, kill proccesses:\n",
739
      $titem->{id}, $trun, join("\n");
740
    print  "#       pid   ppid   pgid   command\n";
741
    print  join("\n",@ktext) . "\n";
742
  }
743
 
744
  # and finally kill all processes of the job
745
  kill 'TERM', @kpids;
746
 
747
  return;
748
}
749
 
750
#-------------------------------------------------------------------------------
751
sub run_tests_multi {
752
  @olist = @tlist;
753
 
754
  while (scalar(@tlist) || scalar(@wlist)) { # while something to do
755
    # start new jobs, if available and job slots free
756
    start_jobs();
757
 
758
    my @fhlist = map { $_->{fh} } @wlist;
759
    my %fdhash;
760
    foreach my $titem (@wlist) {
761
      $fdhash{$titem->{fd}} = $titem;
762
    }
763
 
764
    my $sel = IO::Select->new(@fhlist);
765
    my $neof = 0;
766
    my $time_ticker = gettimeofday() + 0.1;
767
 
768
    while ($neof == 0) {
769
      my $wait_ticker = max(0.1, $time_ticker - gettimeofday() + 0.1);
770
      my @fhlist = $sel->can_read($wait_ticker);
771
      my $time_now = gettimeofday();
772
      if ($time_now >= $time_ticker) {
773
        print_ticker(\@wlist);
774
        $time_ticker = $time_now + 0.9;
775
      }
776
      foreach my $fh (@fhlist) {
777
        my $fd = fileno($fh);
778
        my $titem = $fdhash{$fd};
779
        my $buf = '';
780
        my $nb = sysread $fh, $buf, 1024;
781
 
782
        # data read
783
        if ($nb) {
784
          $titem->{out}    .= $buf;
785
          $titem->{nlines} += ($buf =~ tr/\n/\n/); # count \n in $buf
786
 
787
        # eof or error
788
        } else {
789
          if (defined $titem->{killed}) {
790
            $titem->{out} .= sprintf
791
              "tbrun-I: test killed after %3.1fs\n", $titem->{killed};
792
          }
793
          if (not close $fh) {
794
            my $err = $?;
795
            $titem->{out} .= sprintf
796
              "tbrun-I: test FAILed with exit status %d,%d\n",
797
                ($err>>8), ($err&0xff);
798
            $nfail += 1;
799
            $titem->{exitcode} = $err;
800
          } else {
801
            $titem->{exitcode} = 0;
802
          }
803
 
804
          $neof += 1;
805
          for (my $i=0; $i < scalar(@wlist); $i++) {
806
            next unless $wlist[$i]->{fd} == $fd;
807
            splice @wlist, $i, 1;
808
            last;
809
          }
810
          print_trace($titem) if $opts{trace};
811
        }
812
      } # foreach my $fh ...
813
 
814
      # handle tmax
815
      if (defined $opts{tmax}) {
816
        foreach my $titem (@wlist) {
817
          my $trun = $time_now - $titem->{tstart};
818
          if ($trun > $opts{tmax}) {
819
            kill_job($titem, $trun) unless defined $titem->{killed};
820
          }
821
        }
822
      }
823
 
824
    } # while ($neof == 0)
825
    # here if at least one job finished
826
    print_jobs();
827
  }
828
 
829
  return;
830
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.