diff --git a/nodist/code_coverage b/nodist/code_coverage index 18e8ac368..9762b5331 100755 --- a/nodist/code_coverage +++ b/nodist/code_coverage @@ -6,7 +6,9 @@ use warnings; use Cwd; use File::Copy qw(cp); use File::Path qw(mkpath); +use FindBin qw($RealBin); use Getopt::Long; +use Parallel::Forker; use IO::File; use Pod::Usage; use strict; @@ -19,10 +21,16 @@ our $Remove_Gcda_Regexp; our @Remove_Sources; our @Source_Globs; +our $Fork = new Parallel::Forker (use_sig_child=>1); +$SIG{CHLD} = sub { Parallel::Forker::sig_child($Fork); }; +$SIG{TERM} = sub { $Fork->kill_tree_all('TERM') if $Fork && $Fork->in_parent; die "Quitting...\n"; }; + #====================================================================== # main -our $Opt_Stage = 0; +our $Opt_Fastcov = 0; # out of memory +our $opt_stages = ''; +our %Opt_Stages; autoflush STDOUT 1; autoflush STDERR 1; @@ -30,12 +38,29 @@ Getopt::Long::config("no_auto_abbrev"); if (! GetOptions( "debug" => sub { $Debug = 1; }, "<>" => sub { die "%Error: Unknown parameter: $_[0]\n"; }, - "stage=i" => \$Opt_Stage, # starting stage number + "stages=s" => \$opt_stages, # starting stage number "stop!" => \$Opt_Stop, # stop/do not stop on error in tests )) { die "%Error: Bad usage, try 'install_test --help'\n"; } +{ + my $start = 0; + my $end = 99; + if ($opt_stages && $opt_stages =~ /^(\d+)$/) { + $start = $end = $1; + } elsif ($opt_stages && $opt_stages =~ /^(\d+)-(\d+$)$/) { + $start = $1; $end = $2; + } elsif ($opt_stages && $opt_stages =~ /^-(\d+$)$/) { + $end = $1; + } elsif ($opt_stages && $opt_stages =~ /^(\d+)-$/) { + $start = $1; + } elsif ($opt_stages) { + die "%Error: --stages not understood: $opt_stages,"; + } + for (my $n = $start; $n <= $end; ++$n) { $Opt_Stages{$n} = 1; } +} + test(); exit(0); @@ -45,11 +70,18 @@ sub test { -r "nodist/code_coverage.dat" or die "%Error: Run from the top of the verilator kit,"; require "./nodist/code_coverage.dat"; - if ($Opt_Stage <= 0) { + if ($Opt_Stages{1}) { travis_fold_start("configure"); - print "Stage 0: configure (coverage on)\n"; + print "Stage 1: configure (coverage on)\n"; run("make distclean || true"); + run("autoconf"); run("./configure --enable-longtests CXX='g++ --coverage'"); + travis_fold_end(); + } + + if ($Opt_Stages{2}) { + travis_fold_start("build"); + print "Stage 2: build\n"; run("make -k"); # The optimized versions will not collect good coverage, overwrite them run("cp bin/verilator_bin_dbg bin/verilator_bin"); @@ -57,9 +89,9 @@ sub test { travis_fold_end(); } - if ($Opt_Stage <= 1) { + if ($Opt_Stages{3}) { travis_fold_start("test"); - print "Stage 1: make examples (with coverage on)\n"; + print "Stage 3: make examples (with coverage on)\n"; run("make examples"); run("make test_regress" . ($Opt_Stop ? '' : ' || true')); @@ -67,9 +99,9 @@ sub test { } my $cc_dir = "nodist/obj_dir/coverage"; - if ($Opt_Stage <= 2) { + if ($Opt_Stages{4}) { travis_fold_start("info"); - print "Stage 2: Create info files under $cc_dir\n"; + print "Stage 4: Create info files under $cc_dir\n"; mkpath($cc_dir); mkpath("$cc_dir/info"); my $dats = `find . -print | grep .gcda`; @@ -77,7 +109,6 @@ sub test { foreach my $dat (split '\n', $dats) { $dats{$dat} = 1; } - my %dirs; my %gcnos; foreach my $dat (sort keys %dats) { (my $gcno = $dat) =~ s!\.gcda$!.gcno!; @@ -105,29 +136,71 @@ sub test { warn "MISSING .gcno for a .gcda: $gcno\n"; } } - (my $dir = $dat) =~ s!/[^/]+$!!; - $dirs{$dir} = 1; - } - foreach my $dir (sort keys %dirs) { - (my $outname = $dir) =~ s![^a-zA-Z0-9]+!_!g; - run("cd $cc_dir/info ; lcov -c -d ../../../../$dir -o app_test_${outname}.info"); } travis_fold_end(); } - if ($Opt_Stage <= 3) { + my $Opt_Fastcov = 0; + if ($Opt_Stages{5} && $Opt_Fastcov) { + travis_fold_start("fastcov"); + #Runs out of memory: + #run("${RealBin}/fastcov.py -o $cc_dir/app_total.info"); + #Drops a lot of coverage: + my $dats = `find . -print | grep .gcda`; + my %dirs; + my @dats = sort(split '\n', $dats); + foreach my $dat (@dats) { + (my $dir = $dat) =~ s!/[^/]+$!!; + $dirs{$dir} = 1; + } + my $chunk = 0; + my $comb = ""; + foreach my $dat (@dats) { + $comb .= " -f ../../../../$dat"; + # Need to batch to avoid memory limit + if (length($comb) > 20000 || $dat eq $dats[$#dats]) { + run("cd $cc_dir/info ; ${RealBin}/fastcov.py ${comb} --exclude /usr -o app_chunk_${chunk}.info"); + $comb = ""; + ++$chunk; + } + } + + travis_fold_end(); + } + + if ($Opt_Stages{5} && !$Opt_Fastcov) { + travis_fold_start("infos"); + print "Stage 5: make infos\n"; + my $dats = `find . -print | grep .gcda`; + my %dirs; + foreach my $dat (split '\n', $dats) { + (my $dir = $dat) =~ s!/[^/]+$!!; + $dirs{$dir} = 1; + } + + foreach my $dir (sort keys %dirs) { + (my $outname = $dir) =~ s![^a-zA-Z0-9]+!_!g; + $Fork->schedule(run_on_start => sub { + run("cd $cc_dir/info ; lcov -c -d ../../../../$dir --exclude /usr -o app_test_${outname}.info"); + })->run; + } + $Fork->wait_all; + travis_fold_end(); + } + + if ($Opt_Stages{6}) { travis_fold_start("clone"); # lcov doesn't have a control file to override single lines, so replicate the sources - print "Stage 3: Clone sources under $cc_dir\n"; + print "Stage 6: Clone sources under $cc_dir\n"; clone_sources($cc_dir); travis_fold_end(); } - if ($Opt_Stage <= 4) { + if ($Opt_Stages{8}) { travis_fold_start("copy"); - print "Stage 4: Copy .gcno files\n"; + print "Stage 8: Copy .gcno files\n"; my $dats = `find . -print | grep .gcno`; - foreach my $dat (split '\n', $dats) { + foreach my $dat (sort (split '\n', $dats)) { next if $dat =~ /$cc_dir/; my $outdat = $cc_dir."/".$dat; #print "cp $dat, $outdat);\n"; @@ -136,14 +209,14 @@ sub test { travis_fold_end(); } - if ($Opt_Stage <= 5) { + if ($Opt_Stages{10}) { travis_fold_start("combine"); - print "Stage 5: Combine data files\n"; + print "Stage 10: Combine data files\n"; run("cd $cc_dir ; lcov -c -i -d src/obj_dbg -o app_base.info"); run("cd $cc_dir ; lcov -a app_base.info -o app_total.info"); my $infos = `cd $cc_dir ; find info -print | grep .info`; my $comb = ""; - my @infos = (split /\n/, $infos); + my @infos = (sort (split /\n/, $infos)); foreach my $info (@infos) { $comb .= " -a $info"; # Need to batch them to avoid overrunning shell command length limit @@ -155,9 +228,9 @@ sub test { travis_fold_end(); } - if ($Opt_Stage <= 6) { + if ($Opt_Stages{12}) { travis_fold_start("filter"); - print "Stage 6: Filter processed source files\n"; + print "Stage 12: Filter processed source files\n"; my $cmd = ''; foreach my $glob (@Remove_Sources) { $cmd .= " '$glob'"; @@ -166,24 +239,24 @@ sub test { travis_fold_end(); } - if ($Opt_Stage <= 8) { + if ($Opt_Stages{17}) { travis_fold_start("report"); - print "Stage 7: Create HTML\n"; + print "Stage 17: Create HTML\n"; cleanup_abs_paths($cc_dir, "$cc_dir/app_total.info", "$cc_dir/app_total.info"); run("cd $cc_dir ; genhtml app_total.info --demangle-cpp" ." --rc lcov_branch_coverage=1 --rc genhtml_hi_limit=100 --output-directory html"); travis_fold_end(); } - if ($Opt_Stage <= 9) { + if ($Opt_Stages{18}) { travis_fold_start("upload"); - print "Stage 9: Upload\n"; + print "Stage 18: Upload\n"; my $cmd = "bash <(curl -s https://codecov.io/bash) -f $cc_dir/app_total.info"; print "print: Not running: $cmd\n"; travis_fold_end(); } - if ($Opt_Stage <= 9) { + if ($Opt_Stages{19}) { print "*-* All Finished *-*\n"; } }