Blame maint/getcoverage.in

Packit Service c5cf8c
#! /usr/bin/env perl
Packit Service c5cf8c
# -*- Mode: perl; -*-
Packit Service c5cf8c
#
Packit Service c5cf8c
# This script extracts information from files produced by gcov showing what
Packit Service c5cf8c
# parts of each file have *not* been executed by the test programs.
Packit Service c5cf8c
# 
Packit Service c5cf8c
# Normally, this script should *not* be used directly; instead, use
Packit Service c5cf8c
#     maint/createcoverage
Packit Service c5cf8c
# The createcoverave script uses this (getcoverage) script to create the 
Packit Service c5cf8c
# coverage information.
Packit Service c5cf8c
# 
Packit Service c5cf8c
# To create a coverage report using this script, use the following steps:
Packit Service c5cf8c
#
Packit Service c5cf8c
# configure --enable-coverage <other options>
Packit Service c5cf8c
# make 
Packit Service c5cf8c
# make install
Packit Service c5cf8c
# make testing
Packit Service c5cf8c
# < run other tests, such as the Intel, MPICH1, and C++ tests >
Packit Service c5cf8c
# make coverage
Packit Service c5cf8c
# maint/getcoverage src/mpi src/util > coverage.txt
Packit Service c5cf8c
#
Packit Service c5cf8c
# The script in mpich-tests/getcoverage will perform all of these steps 
Packit Service c5cf8c
# (this script is not yet included with the MPICH distribution)
Packit Service c5cf8c
#
Packit Service c5cf8c
# As an option, create HTML versions of the files with the uncovered code
Packit Service c5cf8c
# highlighted with a different background color (e.g., pale red).  These
Packit Service c5cf8c
# should be placed into the same directory structure as the original source
Packit Service c5cf8c
# files, but with a different root ($annoteSrcDir)
Packit Service c5cf8c
#
Packit Service c5cf8c
# ToDo:
Packit Service c5cf8c
# Another useful option for graphical output would be to keep track of the
Packit Service c5cf8c
# percentage of covered lines and produce an "xdu"-style map of the 
Packit Service c5cf8c
# coverage, with color indicating the fraction of coverage.
Packit Service c5cf8c
#
Packit Service c5cf8c
# The GNU tools sometimes incorrectly mark "break" statements within a 
Packit Service c5cf8c
# case statement as not executed.  As break can also be used in a loop, where
Packit Service c5cf8c
# it is an executable statement, we can't just ignore breaks, and we can't
Packit Service c5cf8c
# determine which type the break is without parsing the program :(.
Packit Service c5cf8c
#
Packit Service c5cf8c
use warnings;
Packit Service c5cf8c
Packit Service c5cf8c
$includeFileInOutput = 0;
Packit Service c5cf8c
$skipErrExits = 1;
Packit Service c5cf8c
$outputUncovered = 1;
Packit Service c5cf8c
@UnCalled = ();
Packit Service c5cf8c
@TopFilenames = ();        # Array of files and directories provided on the
Packit Service c5cf8c
                           # command line
Packit Service c5cf8c
@CoveredFiles = ();        # Array of files that are ok
Packit Service c5cf8c
# annoteSrcDir is where the annotation files are placed,
Packit Service c5cf8c
# annoteBaseDir is the base file name that is stripped from the
Packit Service c5cf8c
# file names
Packit Service c5cf8c
$annoteSrcDir = "";
Packit Service c5cf8c
$annoteWebPrefix = "";
Packit Service c5cf8c
$CoverageMessage = "";     # Extra message for the coverage index file
Packit Service c5cf8c
$indexfile = "";     # File to contain the index of files
Packit Service c5cf8c
$annoteFiles = 0;
Packit Service c5cf8c
$annoteBaseDir = "";
Packit Service c5cf8c
%AnnoteFilesMap = ();   # Clear the list of files 
Packit Service c5cf8c
%AnnoteMissed   = ();   # Clear the mapping of filenames to "missed;total" lines
Packit Service c5cf8c
%AnnoteFilesToOrig = (); # Map from annoted file key to original name (used for
Packit Service c5cf8c
                         # indexing into AnnoteMissed)
Packit Service c5cf8c
$gDebug = 0;
Packit Service c5cf8c
$gDebugDetail = 0;
Packit Service c5cf8c
Packit Service c5cf8c
# The line leaders are used to provide a common set of expressions to match
Packit Service c5cf8c
# the begining of a line.  This matches the output from the gcov tools.
Packit Service c5cf8c
# There are two of these because the output from the tools associtated with
Packit Service c5cf8c
# gcc version 2.x and 3.x are different.
Packit Service c5cf8c
# As, apparently is 4.x.
Packit Service c5cf8c
# We also permit an isolated open or close brace on the line (a non-executable
Packit Service c5cf8c
# statement)
Packit Service c5cf8c
$lineLeader2 = '^\s*[\{\}]?\s*';
Packit Service c5cf8c
$lineLeader3 = '^\s*-:\s*\d+:\s*[\{\}]?\s*';
Packit Service c5cf8c
$lineLeader4 = '^\s*[0-9-]+:\s*\d+:\s*[\{\}]?\s*';
Packit Service c5cf8c
#
Packit Service c5cf8c
# This is the list of known block names.  They represent several types of
Packit Service c5cf8c
# code for which it may be desirable not to count in coverage analysis.
Packit Service c5cf8c
#  Code that normally should not be executed:
Packit Service c5cf8c
#    DEBUG - Code for debugging
Packit Service c5cf8c
#    EXPERIMENTAL - Experimental code that will normally not be executed
Packit Service c5cf8c
#  Code that may be executed, but for which it is difficult to write 
Packit Service c5cf8c
#  coverage tests
Packit Service c5cf8c
#    USEREXTENSION - Code that provides for user extensions, particularly 
Packit Service c5cf8c
#         through places where user-provided functions may be called
Packit Service c5cf8c
#    OPTIONALINIT - Code that provides for lazy initialization of 
Packit Service c5cf8c
#         datastructures, particularly function tables.  
Packit Service c5cf8c
#  Code that is executed only with erroneous input (e.g., user errors) or
Packit Service c5cf8c
#  exceptional events (e.g., socket unexpectedly closes).
Packit Service c5cf8c
#    ERROR HANDLING - Code for handling or reporting errors
Packit Service c5cf8c
@BlockNames = ( "DEBUG", "ERROR HANDLING", "EXPERIMENTAL", "USEREXTENSION",
Packit Service c5cf8c
    "OPTIONALINIT" );
Packit Service c5cf8c
#
Packit Service c5cf8c
# Keep track of coverage amounts
Packit Service c5cf8c
$GrandTotal       = 0;
Packit Service c5cf8c
$GrandTotalMissed = 0;
Packit Service c5cf8c
$GrandTotalExec   = 0;
Packit Service c5cf8c
Packit Service c5cf8c
# gfilename is used to hold the current open file for READING, so
Packit Service c5cf8c
# that error messages can be more specific
Packit Service c5cf8c
$gFilename = "";
Packit Service c5cf8c
Packit Service c5cf8c
# ----------------------------------------------------------------------------
Packit Service c5cf8c
for (@ARGV) {
Packit Service c5cf8c
    if (/-webpages=(.*)/) {
Packit Service c5cf8c
	# Create the coverage in default web pages for the device
Packit Service c5cf8c
	# named as an argument (e.g., maint/getcoverage -webpages=ch3:sock)
Packit Service c5cf8c
	my $device = $1;
Packit Service c5cf8c
	my $weblocbase = "@abs_mpichsrcdir@/www/coverage";
Packit Service c5cf8c
	my $webloc     = $weblocbase . "/$device";
Packit Service c5cf8c
	$annoteSrcDir = $webloc;
Packit Service c5cf8c
	$annoteFiles  = 1;
Packit Service c5cf8c
	$annoteWebPrefix = "";
Packit Service c5cf8c
	$indexdir = $webloc;
Packit Service c5cf8c
	$indexfile = "$indexdir/index.htm";
Packit Service c5cf8c
	$coveredindexfile = "$indexdir/covered.htm";
Packit Service c5cf8c
	if (! -d $indexdir) { &MakeDirs( $indexfile ); }
Packit Service c5cf8c
	next; 
Packit Service c5cf8c
    }
Packit Service c5cf8c
    elsif (/-annote=(.*)/) {
Packit Service c5cf8c
	# Create an annotation of the source data in the specified root
Packit Service c5cf8c
	# directory
Packit Service c5cf8c
	$annoteSrcDir = $1;
Packit Service c5cf8c
	$annoteFiles  = 1;
Packit Service c5cf8c
	next;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    elsif (/-annoteweb=(.*)/) {
Packit Service c5cf8c
	# annoteweb gives the URL that matches the annoteSrcDir.
Packit Service c5cf8c
	$annoteWebPrefix = $1;
Packit Service c5cf8c
	next;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    elsif (/-index=(.*)/) {
Packit Service c5cf8c
	$indexfile = $1;
Packit Service c5cf8c
	next;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    elsif (/-indexdir=(.*)/) {
Packit Service c5cf8c
	# This target sets the directory for the index file, along with
Packit Service c5cf8c
	# the file that contains the names of the files that are 
Packit Service c5cf8c
	# considered completely covered.
Packit Service c5cf8c
	my $indexdir = $1;
Packit Service c5cf8c
	$indexfile = "$indexdir/index.htm";
Packit Service c5cf8c
	$coveredindexfile = "$indexdir/covered.htm";
Packit Service c5cf8c
	if (! -d $indexdir) { &MakeDirs( $indexfile ); }
Packit Service c5cf8c
	next;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    elsif (/-coveredmsg=(.*)/) {
Packit Service c5cf8c
	$CoverageMessage = $1;
Packit Service c5cf8c
	next;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    elsif (/-debugall/) {
Packit Service c5cf8c
	$gDebugDetail = 1;
Packit Service c5cf8c
	$gDebug       = 1;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    elsif (/-debug/) { 
Packit Service c5cf8c
    	$gDebug = 1;
Packit Service c5cf8c
	next;
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    $TopFilenames[$#TopFilenames + 1] = $_;
Packit Service c5cf8c
}
Packit Service c5cf8c
# ----------------------------------------------------------------------------
Packit Service c5cf8c
Packit Service c5cf8c
# ----------------------------------------------------------------------------
Packit Service c5cf8c
Packit Service c5cf8c
for my $filename (@TopFilenames) {
Packit Service c5cf8c
    if (-d $filename) {
Packit Service c5cf8c
	# Check for a "pmpi" directory - one that was used to merge 
Packit Service c5cf8c
	# PMPI and MPI outputs.  If found, we skip to the next directory
Packit Service c5cf8c
	if ($filename =~ /\-pmpi$/) { next; }
Packit Service c5cf8c
	# Added to handle a change in the naming convention for object files
Packit Service c5cf8c
	# in MPICH, which now needs a -mpi directory as well as the -pmpi
Packit Service c5cf8c
	# directory needed to handle the pmpi object files.
Packit Service c5cf8c
	if ($filename =~ /\-mpi$/) { next; }
Packit Service c5cf8c
	# Expand the directory into a list of files.  If there are 
Packit Service c5cf8c
	# subdirectories, expand them too (e.g., get a list of 
Packit Service c5cf8c
	# *every* file within the directory tree. 
Packit Service c5cf8c
	@files = &ExpandDir( $filename );
Packit Service c5cf8c
	foreach my $file (@files) {
Packit Service c5cf8c
	    ($missed_lines,$total_lines,$execLines) = 
Packit Service c5cf8c
		&CountUncoveredLines( $file );
Packit Service c5cf8c
	    $GrandTotal       += $total_lines;
Packit Service c5cf8c
	    $GrandTotalMissed += $missed_lines;
Packit Service c5cf8c
	    $GrandTotalExec   += $execLines;
Packit Service c5cf8c
	    $AnnoteMissed{$file} = "$missed_lines;$total_lines;$execLines";
Packit Service c5cf8c
	    if ($missed_lines) {
Packit Service c5cf8c
		print "$missed_lines line(s) not covered by tests in $file\n";
Packit Service c5cf8c
		&WriteAnnoteFile( $file, $total_lines, $execLines, $missed_lines );
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    else {
Packit Service c5cf8c
		print "All code covered by tests in $file\n";
Packit Service c5cf8c
		$CoveredFiles[$#CoveredFiles+1] = $file;
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	}
Packit Service c5cf8c
    }
Packit Service c5cf8c
    elsif (-s $filename) {
Packit Service c5cf8c
	($missed_lines,$total_lines,$execLines) = 
Packit Service c5cf8c
	    &CountUncoveredLines( $filename );
Packit Service c5cf8c
	$GrandTotal       += $total_lines;
Packit Service c5cf8c
	$GrandTotalMissed += $missed_lines;
Packit Service c5cf8c
	$GrandTotalExec   += $execLines;
Packit Service c5cf8c
	$AnnoteMissed{$filename} = "$missed_lines;$total_lines;$execLines";
Packit Service c5cf8c
	print "$filename - $missed_lines missed lines\n" if $gDebug;
Packit Service c5cf8c
	if ($missed_lines) {
Packit Service c5cf8c
	    print "$missed_lines line(s) not covered by tests in $filename\n";
Packit Service c5cf8c
	    &WriteAnnoteFile( $filename, $total_lines, $execLines, $missed_lines );
Packit Service c5cf8c
	}
Packit Service c5cf8c
	else {
Packit Service c5cf8c
	    print "All code covered by tests in $filename\n";
Packit Service c5cf8c
	    $CoveredFiles[$#CoveredFiles+1] = $filename;
Packit Service c5cf8c
	}
Packit Service c5cf8c
    }
Packit Service c5cf8c
    else {
Packit Service c5cf8c
	print "Cannot open $filename\n";
Packit Service c5cf8c
    }
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
for $routine (@UnCalled) {
Packit Service c5cf8c
    print STDERR "$routine never called\n";
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
if ($annoteFiles && $indexfile ne "") {
Packit Service c5cf8c
    # Here is where we could process the list of files that were annotated
Packit Service c5cf8c
    &WriteAnnoteFileMap( $indexfile );
Packit Service c5cf8c
    if ($coveredindexfile ne "") {
Packit Service c5cf8c
	WriteCoveredFileMap( $coveredindexfile );
Packit Service c5cf8c
    }
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
# ----------------------------------------------------------------------------
Packit Service c5cf8c
# Here begin the support routines
Packit Service c5cf8c
# ----------------------------------------------------------------------------
Packit Service c5cf8c
# ?Count the number of uncovered lines, and return that number.
Packit Service c5cf8c
sub CountUncoveredLines {
Packit Service c5cf8c
    my $filename = $_[0];
Packit Service c5cf8c
    my $missed_lines = 0;
Packit Service c5cf8c
    my $headerOutput = 0;
Packit Service c5cf8c
    my $linecount    = 0;    # Line in the coverage file
Packit Service c5cf8c
    my $fileline     = 0;    # Line in the original file
Packit Service c5cf8c
    my $executableLines = 0; # Number of lines that could be executed
Packit Service c5cf8c
    my $lastLineOut  = -1;
Packit Service c5cf8c
    my ($lineincr, $filelineincr); # Used to update linecount,fileline
Packit Service c5cf8c
    my $oldLine;
Packit Service c5cf8c
    my $lastLine;
Packit Service c5cf8c
Packit Service c5cf8c
    open( FD, "<$filename" ) || die "Could not open $filename\n";
Packit Service c5cf8c
    $gFilename = $filename;   # Export filename for error reporting
Packit Service c5cf8c
Packit Service c5cf8c
    # Note that linecount is relative to the foo.c.gcov file, not the
Packit Service c5cf8c
    # original foo.c file.
Packit Service c5cf8c
    # Gcc 3.x changed the output format (including a line number and
Packit Service c5cf8c
    # a - for unexecuted lines, i.e.,
Packit Service c5cf8c
    # ^\s*-:\s*\d+:original-text-line
Packit Service c5cf8c
    
Packit Service c5cf8c
L:  while (<FD>) {
Packit Service c5cf8c
	$linecount++;
Packit Service c5cf8c
	# Coverage messages appear to always begin in the first column.
Packit Service c5cf8c
	if (/^\s/) { $fileline++; }
Packit Service c5cf8c
	# Skip any error checking block
Packit Service c5cf8c
	if (/^\s*#\s*ifdef\s+HAVE_ERROR_CHECKING/ ||
Packit Service c5cf8c
	    /^\s*-:\s*\d+:\s*#\s*ifdef\s+HAVE_ERROR_CHECKING/) {
Packit Service c5cf8c
	    while (<FD>) {
Packit Service c5cf8c
		$linecount++;
Packit Service c5cf8c
		if (/^\s/) { $fileline++; }
Packit Service c5cf8c
		if (/^\s+#\s*endif/ || /^\s*#\s*els/ ||
Packit Service c5cf8c
		    /^\s*-:\s*\d+:\s*#\s*endif/ || /^\s*-:\s*\d+:\s*#\s*els/) { 
Packit Service c5cf8c
		    last; 
Packit Service c5cf8c
		}
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    next;	       
Packit Service c5cf8c
	}
Packit Service c5cf8c
Packit Service c5cf8c
	# Skip any blocks marked as debugging or unavoidable error checking
Packit Service c5cf8c
	# code 
Packit Service c5cf8c
	foreach my $name (@BlockNames) {
Packit Service c5cf8c
	    if (/$lineLeader2\/\*\s+--BEGIN $name--\s+\*\// ||
Packit Service c5cf8c
		/$lineLeader3\/\*\s+--BEGIN $name--\s+\*\// ||
Packit Service c5cf8c
		/$lineLeader4\/\*\s+--BEGIN $name--\s+\*\//) {
Packit Service c5cf8c
		# It is usually an error to encounter a block begin 
Packit Service c5cf8c
		# within another block.  The exception is the
Packit Service c5cf8c
		# EXPERIMENTAL block, which may contain ERROR HANDLING blocks
Packit Service c5cf8c
		my $allowNest = 0;
Packit Service c5cf8c
		if ($name eq "EXPERIMENTAL") { $allowNest = 1; }
Packit Service c5cf8c
		($lineincr,$filelineincr) = 
Packit Service c5cf8c
		    &skipBlock( $name, $allowNest, $linecount );
Packit Service c5cf8c
		$linecount += $lineincr;
Packit Service c5cf8c
		$fileline += $filelineincr;
Packit Service c5cf8c
		next L;
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	}
Packit Service c5cf8c
Packit Service c5cf8c
	# If requested, skip obvious error checking lines
Packit Service c5cf8c
	# The errflag set should really be an MPIR_ERR macro; the
Packit Service c5cf8c
	# test is designed to accept only lines that set that flag
Packit Service c5cf8c
	# and nothing else.
Packit Service c5cf8c
	if ($skipErrExits && 
Packit Service c5cf8c
	    (/FUNC_EXIT.*STATE/ || /MPIR_Err_return_/ || 
Packit Service c5cf8c
	     /MPIR_ERR_SET/ || /MPIR_ERR_POP/ || /goto\s+fn_fail/ ||
Packit Service c5cf8c
	     /MPIR_ERR_ADD/ || /\*errflag\s*=\s*[^;]*;\s*$/ || 
Packit Service c5cf8c
	     /fn_fail:/ || /MPIR_Err_create_code/)) {
Packit Service c5cf8c
	    next;
Packit Service c5cf8c
	}
Packit Service c5cf8c
Packit Service c5cf8c
	# Gcc 3.3 generates a different output form (!) with only
Packit Service c5cf8c
	# 5 sharps instead of 6.  It does mark lines that are not 
Packit Service c5cf8c
	# executable with a -, which is an improvement
Packit Service c5cf8c
	if (/^\s*######?/) {
Packit Service c5cf8c
	    # First, ignore a few odd markings by gcov
Packit Service c5cf8c
	    if (/^\s*######?\s*\}\s*$/ || /^\s*######?\s*:\s*\d+:\s*\}\s*$/) {
Packit Service c5cf8c
		# Ignore a closed brace
Packit Service c5cf8c
		next;
Packit Service c5cf8c
	    }
Packit Service c5cf8c
Packit Service c5cf8c
	    $missed_lines++;
Packit Service c5cf8c
	    $executableLines++;  # This missed line is executable 
Packit Service c5cf8c
		
Packit Service c5cf8c
	    # The char\s+.* allows char or const char (or other 
Packit Service c5cf8c
	    # things, but nothing else should use FCNAME).
Packit Service c5cf8c
	    if (/static\s+char\s+.*FCNAME\[\]\s*=\s*\"(.*)\"/) {
Packit Service c5cf8c
		# Add this to a list of functions never called.
Packit Service c5cf8c
		$UnCalled[$#UnCalled + 1] = $1;
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    if ($outputUncovered) {
Packit Service c5cf8c
		if (!$headerOutput) {
Packit Service c5cf8c
		    print "\nUncovered lines in $filename\n";
Packit Service c5cf8c
		    $headerOutput = 1;
Packit Service c5cf8c
		}
Packit Service c5cf8c
		if ($lastLineOut < $linecount - 2) {
Packit Service c5cf8c
		    my $ll = $linecount - 2;
Packit Service c5cf8c
		    print "$ll:\t$oldLine";
Packit Service c5cf8c
		}
Packit Service c5cf8c
		if ($lastLineOut < $linecount - 1) {
Packit Service c5cf8c
		    my $ll = $linecount - 1;
Packit Service c5cf8c
		    print "$ll:\t$lastLine";
Packit Service c5cf8c
		}
Packit Service c5cf8c
		print "$linecount:\t$_";
Packit Service c5cf8c
		$lastLineOut = $linecount;
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	}
Packit Service c5cf8c
	elsif (/^\s*-:/) {
Packit Service c5cf8c
	    ;
Packit Service c5cf8c
	    # Not (at least to gcov) an executable line
Packit Service c5cf8c
	}
Packit Service c5cf8c
	elsif (/^\s*\d+:/) {
Packit Service c5cf8c
	    # A line that was executed at least once
Packit Service c5cf8c
	    $executableLines++;
Packit Service c5cf8c
	}
Packit Service c5cf8c
	if ($includeFileInOutput) {
Packit Service c5cf8c
	    print $_;
Packit Service c5cf8c
	}
Packit Service c5cf8c
	$oldLine = $lastLine;
Packit Service c5cf8c
	$lastLine = $_;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    close (FD);
Packit Service c5cf8c
    return ($missed_lines,$fileline,$executableLines);
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
# ----------------------------------------------------------------------------
Packit Service c5cf8c
# ?Annotate a file by placing the uncovered lines in bgcolor
Packit Service c5cf8c
# AnnotateUncoveredLines( coveragefile, bgcolor, filenhandle )
Packit Service c5cf8c
#
Packit Service c5cf8c
# This uses a state machine to move between the states of:
Packit Service c5cf8c
#   init - beginning (top of file)
Packit Service c5cf8c
#   unex - within unexecuted code
Packit Service c5cf8c
#   exec - within executed code
Packit Service c5cf8c
#   skip - within skipped code (code that we don't care whether it is
Packit Service c5cf8c
#          executed).  Currently contains 3 sub states:
Packit Service c5cf8c
#             errcheck         (error checking code)
Packit Service c5cf8c
#             ERROR HANDLING   (error handling code)
Packit Service c5cf8c
#             DEBUG            (debug code)
Packit Service c5cf8c
#             EXPERIMENTAL     (experimental)
Packit Service c5cf8c
#             USEREXTENSION    (code to enable user extensions)
Packit Service c5cf8c
#          The uppercase names are the same as the @BlockNames.
Packit Service c5cf8c
#          Currently, the skipcolors are all lightblue
Packit Service c5cf8c
sub AnnotateUncoveredLines {
Packit Service c5cf8c
    my $filename = $_[0];
Packit Service c5cf8c
    my $bgcolor  = $_[1];
Packit Service c5cf8c
    my %skipcolors = ( "errcheck" => "lightblue",
Packit Service c5cf8c
		       "ERROR HANDLING" => "lightblue",
Packit Service c5cf8c
		       "DEBUG" => "lightblue", 
Packit Service c5cf8c
		       "EXPERIMENTAL" => "lightblue",
Packit Service c5cf8c
		       "USEREXTENSION" => "lightyellow", 
Packit Service c5cf8c
		       "OPTIONALINIT"  => "lightgreen",
Packit Service c5cf8c
		       );
Packit Service c5cf8c
    my $outfile  = $_[2];
Packit Service c5cf8c
    my $curstate = "init";    # Current state
Packit Service c5cf8c
    my $newstate;             # New state implied by the last line read
Packit Service c5cf8c
    my $substate;             # Used for substates within a state (skip only)
Packit Service c5cf8c
    my $newline  = "\r\n";
Packit Service c5cf8c
Packit Service c5cf8c
    open( FD, "<$filename" ) || die "Could not open $filename\n";
Packit Service c5cf8c
    $gFilename = $filename;
Packit Service c5cf8c
Packit Service c5cf8c
    print $outfile "$newline";
Packit Service c5cf8c
Packit Service c5cf8c
    # Note that linecount is relative to the foo.c.gcov file, not the
Packit Service c5cf8c
    # original foo.c file.
Packit Service c5cf8c
L2: while (<FD>) {
Packit Service c5cf8c
	# Skip coverage messages (always start in the first column) 
Packit Service c5cf8c
	if (! /^\s/) { next; }
Packit Service c5cf8c
Packit Service c5cf8c
	# TODO:
Packit Service c5cf8c
	# If there is neither a "######" nor a count in front of the
Packit Service c5cf8c
	# line, and it is not a comment or blank space, it has
Packit Service c5cf8c
	# the same state as currently in (e.g., it is probably a 
Packit Service c5cf8c
	# continuation line).
Packit Service c5cf8c
	# Determine what state this line is in
Packit Service c5cf8c
	# (gcc 3.3 only outputs 5 sharps, earlier versions use 6)
Packit Service c5cf8c
	# The format of the line also changed
Packit Service c5cf8c
	if ($skipErrExits && 
Packit Service c5cf8c
	    (/FUNC_EXIT.*STATE/ || /MPIR_Err_return_/ || 
Packit Service c5cf8c
	     /MPIR_ERR_SET/ || /MPIR_ERR_POP/ || /goto\s+fn_fail/ ||
Packit Service c5cf8c
	     /MPIR_ERR_ADD/ || /\*errflag\s*=\s*[^;]*;\s*$/ || 
Packit Service c5cf8c
            /fn_fail:/ || /MPIR_Err_create_code/)) {
Packit Service c5cf8c
	    # If requested, skip obvious error checking lines
Packit Service c5cf8c
	    $newstate = "skip";
Packit Service c5cf8c
	    $substate = "errcheck";
Packit Service c5cf8c
	}
Packit Service c5cf8c
	elsif (/^\s*######?/) {
Packit Service c5cf8c
	    $newstate = "unex";
Packit Service c5cf8c
	}
Packit Service c5cf8c
	elsif (/^\s*\d+\s/ ||
Packit Service c5cf8c
	       /^\s*\d+:\s*\d+:/) {
Packit Service c5cf8c
	    # Also had /^\s*-:\s*\d+:/, but this should really be "same state"
Packit Service c5cf8c
	    # to avoid flipping in and out of a state.
Packit Service c5cf8c
	    $newstate = "exec";
Packit Service c5cf8c
	}
Packit Service c5cf8c
	else {
Packit Service c5cf8c
	    # Keep the same state...
Packit Service c5cf8c
	    if ($curstate eq "init") {
Packit Service c5cf8c
		$newstate = "exec";
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    else {
Packit Service c5cf8c
		$newstate = $curstate;
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	}
Packit Service c5cf8c
Packit Service c5cf8c
	# Special cases for blocks that we skip (mark them as executed) 
Packit Service c5cf8c
	if (/^\s*#\s*ifdef\s+HAVE_ERROR_CHECKING/ ||
Packit Service c5cf8c
	    /^\s*-:\s*\d+:\s*#\s*ifdef\s+HAVE_ERROR_CHECKING/) {
Packit Service c5cf8c
	    $newstate = "skip";
Packit Service c5cf8c
	    $substate = "errcheck";
Packit Service c5cf8c
	}
Packit Service c5cf8c
	else {
Packit Service c5cf8c
	    foreach my $name (@BlockNames) {
Packit Service c5cf8c
		if (/$lineLeader2\/\*\s+--BEGIN $name--\s+\*\// ||
Packit Service c5cf8c
		    /$lineLeader3\/\*\s+--BEGIN $name--\s+\*\// ||
Packit Service c5cf8c
		    /$lineLeader4\/\*\s+--BEGIN $name--\s+\*\//) {
Packit Service c5cf8c
		    $newstate = "skip";
Packit Service c5cf8c
		    $substate = $name;
Packit Service c5cf8c
		    last;
Packit Service c5cf8c
		}
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	}
Packit Service c5cf8c
Packit Service c5cf8c
	# If there is a change in state, generate the correct code
Packit Service c5cf8c
	if ($newstate ne $curstate) {
Packit Service c5cf8c
	    print STDERR "Newstate = $newstate\n" if $gDebugDetail;
Packit Service c5cf8c
	    if ($curstate ne "init") {
Packit Service c5cf8c
		# Finish off the current state
Packit Service c5cf8c
		print $outfile "$newline";
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    if ($newstate eq "exec")    { $bgcolor = "white"; } 
Packit Service c5cf8c
	    elsif ($newstate eq "unex") { $bgcolor = "red"; }
Packit Service c5cf8c
	    elsif ($newstate eq "skip") {
Packit Service c5cf8c
		$bgcolor = $skipcolors{$substate};
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    else {
Packit Service c5cf8c
		print STDERR "Internal error: unrecognized state $newstate\n";
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    print $outfile "
$newline";
Packit Service c5cf8c
	}
Packit Service c5cf8c
	$curstate = $newstate;
Packit Service c5cf8c
	
Packit Service c5cf8c
	# Add this line
Packit Service c5cf8c
	print $outfile &HTMLify($_);
Packit Service c5cf8c
Packit Service c5cf8c
	# Now, for the blocks that we skip, read them until we reach the
Packit Service c5cf8c
	# end of the block
Packit Service c5cf8c
	# Skip any error checking block
Packit Service c5cf8c
	if (/^\s*#\s*ifdef\s+HAVE_ERROR_CHECKING/ ||
Packit Service c5cf8c
	    /^\s*-:\s*\d+:\s*#\s*ifdef\s+HAVE_ERROR_CHECKING/) {
Packit Service c5cf8c
	    my $sawend = 0;
Packit Service c5cf8c
	    print STDERR "Skipping HAVE_ERROR_CHECKING\n" if $gDebugDetail; 
Packit Service c5cf8c
	    while (<FD>) {
Packit Service c5cf8c
		if (!/^\s/) { next; }
Packit Service c5cf8c
	        print $outfile &HTMLify($_);
Packit Service c5cf8c
		if (/^\s+#\s*endif/ || /^\s*#\s*els/ ||
Packit Service c5cf8c
		    /^\s*-:\s*\d+:\s*#\s*endif/ || 
Packit Service c5cf8c
		    /^\s*-:\s*\d+:\s*#\s*els/) { 
Packit Service c5cf8c
		    $sawend = 1;
Packit Service c5cf8c
		    last; 
Packit Service c5cf8c
		}
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    if (!$sawend) {
Packit Service c5cf8c
		print STDERR "File $gFilename ended in HAVE ERROR CHECKING block\n";
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    next;	       
Packit Service c5cf8c
	}
Packit Service c5cf8c
	# Skip any blocks marked as debugging or unavoidable error checking
Packit Service c5cf8c
	# code 
Packit Service c5cf8c
	foreach my $name (@BlockNames) {
Packit Service c5cf8c
	    if (/$lineLeader2\/\*\s+--BEGIN $name--\s+\*\// ||
Packit Service c5cf8c
		/$lineLeader3\/\*\s+--BEGIN $name--\s+\*\// ||
Packit Service c5cf8c
		/$lineLeader4\/\*\s+--BEGIN $name--\s+\*\//) {
Packit Service c5cf8c
		&skipBlockAndAnnotate( $outfile, $name );
Packit Service c5cf8c
		next L2;	       
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	}
Packit Service c5cf8c
Packit Service c5cf8c
Packit Service c5cf8c
    }
Packit Service c5cf8c
    close (FD);
Packit Service c5cf8c
    print $outfile "$newline$newline";
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
# Write the annotation file as a simple HTML file
Packit Service c5cf8c
# (returns immediately if annotations are not requested)
Packit Service c5cf8c
# To aid in navigating the annotation pages
Packit Service c5cf8c
# 
Packit Service c5cf8c
# WriteAnnoteFile( filename, total_lines, exec_lines, missed_lines )
Packit Service c5cf8c
# Also inserts the generated filename to the hash %AnnoteFilesMap
Packit Service c5cf8c
# with key the base filename (e.g., foo.c) and the value the
Packit Service c5cf8c
# full directory path (e.g., $annoteSrcDir/foo.c.htm)
Packit Service c5cf8c
sub WriteAnnoteFile {
Packit Service c5cf8c
    my $filename = $_[0];
Packit Service c5cf8c
    my $totalLines = $_[1];
Packit Service c5cf8c
    my $execLines  = $_[2];
Packit Service c5cf8c
    my $missedLines = $_[3];
Packit Service c5cf8c
Packit Service c5cf8c
    print "Writing (if $annoteFiles) annotated file $filename\n" if $gDebug;
Packit Service c5cf8c
    if ($annoteFiles) {
Packit Service c5cf8c
	# Make a file name
Packit Service c5cf8c
	my $basefile = $filename;
Packit Service c5cf8c
	$basefile =~ s/\.merge$//;    # Remove "merge" from filename
Packit Service c5cf8c
	$basefile =~ s/\.gcov//;
Packit Service c5cf8c
	$basefile =~ s/$annoteBaseDir//;
Packit Service c5cf8c
	my $OUTFD = OUTFD;
Packit Service c5cf8c
	my $rc = &MakeDirs( "$annoteSrcDir/$basefile" );
Packit Service c5cf8c
	if ($rc == 0) {
Packit Service c5cf8c
	    print STDERR "Could not create directories $annoteSrcDir/$basefile\n";
Packit Service c5cf8c
	    return;
Packit Service c5cf8c
	}
Packit Service c5cf8c
	print STDERR "Opening $annoteSrcDir/$basefile.htm\n" if $gDebug;
Packit Service c5cf8c
	$rc = open( $OUTFD, ">$annoteSrcDir/$basefile.htm" );
Packit Service c5cf8c
	if ($rc != 0) {
Packit Service c5cf8c
	    $AnnoteFilesMap{$basefile} = "$annoteSrcDir/$basefile.htm";
Packit Service c5cf8c
	    $AnnoteFilesToOrig{$basefile} = $filename;
Packit Service c5cf8c
	    print STDERR "Saving $filename as AnnoteFilesToOrig{$basefile}\n" if $gDebug;
Packit Service c5cf8c
	    $newline = "\r\n";
Packit Service c5cf8c
	    print $OUTFD "<HTML><HEAD>$newline" || die "writing header";
Packit Service c5cf8c
	    print $OUTFD "<TITLE>Coverage for $filename</TITLE>$newline";
Packit Service c5cf8c
	    print $OUTFD "</HEAD>$newline";
Packit Service c5cf8c
	    print $OUTFD "<BODY BGCOLOR=\"FFFFFF\">$newline";
Packit Service c5cf8c
	    my $percent = sprintf( "%2d", 100 * $missedLines / $execLines );
Packit Service c5cf8c
	    print $OUTFD "For $filename, $missedLines lines of code out of $execLines executable lines were not executed ($percent\% missed)

$newline";

Packit Service c5cf8c
	    
Packit Service c5cf8c
	    &AnnotateUncoveredLines( $filename, "red", $OUTFD );
Packit Service c5cf8c
	    print $OUTFD "</BODY></HTML>$newline";
Packit Service c5cf8c
	    close( $OUTFD );
Packit Service c5cf8c
	}
Packit Service c5cf8c
	else {
Packit Service c5cf8c
	    print STDERR "Cannot open $annoteSrcDir/$basefile\n";
Packit Service c5cf8c
	}
Packit Service c5cf8c
    }
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
#
Packit Service c5cf8c
# TODO: Create a map of the annotated files by using AnnoteFilesMap
Packit Service c5cf8c
# We need a better framework for this.  Perhaps a table with multiple
Packit Service c5cf8c
# columns.  We could also arrange in groups by directory names
Packit Service c5cf8c
#
Packit Service c5cf8c
# WriteAnnoteFileMap( name-fo-file-to-write )
Packit Service c5cf8c
sub WriteAnnoteFileMap {
Packit Service c5cf8c
    my @names = sort(keys(%AnnoteFilesMap));
Packit Service c5cf8c
    my $indexfile = $_[0];
Packit Service c5cf8c
    #my $date = `date "+%Y-%m-%d-%H-%M"`;
Packit Service c5cf8c
    my $date = `date`;
Packit Service c5cf8c
    # FilesByMissed is a semicolon separated list of files, indexed by the
Packit Service c5cf8c
    # number of lines missed.  This will allow us to list the top problems
Packit Service c5cf8c
    # in terms of the number of uncovered lines.
Packit Service c5cf8c
    my %FilesByMissed = ();
Packit Service c5cf8c
Packit Service c5cf8c
    open (IFD, ">$indexfile" ) || die "Could not open $indexfile\n";
Packit Service c5cf8c
Packit Service c5cf8c
    print IFD "<HTML><HEAD><TITLE>Index to coverage analysis</TITLE></HEAD>\n";
Packit Service c5cf8c
    print IFD "<BODY BGCOLOR=\"FFFFFF\">\n";
Packit Service c5cf8c
Packit Service c5cf8c
    # Create the heading
Packit Service c5cf8c
    if ($GrandTotal > 0 && $GrandTotalMissed > 0) { 
Packit Service c5cf8c
	print IFD "

Summary of Coverage Testing

\n";
Packit Service c5cf8c
	print IFD "Of $GrandTotal lines in code, $GrandTotalMissed lines were not covered\n";
Packit Service c5cf8c
	my $percent = (100 * $GrandTotalMissed) / $GrandTotal;
Packit Service c5cf8c
	my $covered = 100 - $percent;
Packit Service c5cf8c
	# convert to strings
Packit Service c5cf8c
        $percent = sprintf( "%.2f", $percent );
Packit Service c5cf8c
	$covered = sprintf( "%.2f", $covered );
Packit Service c5cf8c
	print IFD "or $percent% missed ($covered% covered)\n";
Packit Service c5cf8c
	# Do the same, but for the executable lines
Packit Service c5cf8c
	print IFD "
Of $GrandTotalExec executable lines in code, $GrandTotalMissed lines were not covered\n";
Packit Service c5cf8c
	$percent = (100 * $GrandTotalMissed) / $GrandTotalExec;
Packit Service c5cf8c
	$covered = 100 - $percent;
Packit Service c5cf8c
	# convert to strings
Packit Service c5cf8c
        $percent = sprintf( "%.2f", $percent );
Packit Service c5cf8c
	$covered = sprintf( "%.2f", $covered );
Packit Service c5cf8c
	print IFD "or $percent% missed ($covered% covered)\n";
Packit Service c5cf8c
	
Packit Service c5cf8c
	my $date = `date "+%Y-%m-%d-%H-%M"`;
Packit Service c5cf8c
	print IFD "
This index created on $date\n";
Packit Service c5cf8c
	if ($CoverageMessage ne "") { 
Packit Service c5cf8c
	    print IFD "
$CoverageMessage
\n"
Packit Service c5cf8c
	}
Packit Service c5cf8c
	print IFD "

\n";

Packit Service c5cf8c
	print IFD "The following files contained some code that was not executed.  Files in which all non-error-handling, non-debug code was executed are not shown\n"
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    my $col = 1;
Packit Service c5cf8c
    my $maxcol = 4;
Packit Service c5cf8c
    my $curprefix = "--NONE--";
Packit Service c5cf8c
    for ($i=0; $i<=$#names; $i++) {
Packit Service c5cf8c
	my $file = $names[$i];
Packit Service c5cf8c
	my $target = $AnnoteFilesMap{$file};
Packit Service c5cf8c
Packit Service c5cf8c
	# Clean up filename
Packit Service c5cf8c
	$file =~ s/^\.\///;
Packit Service c5cf8c
	if ($file =~ /(.*)\/([^\/]*)/) {
Packit Service c5cf8c
	    $dirname  = $1;
Packit Service c5cf8c
	    $basename = $2;
Packit Service c5cf8c
	}
Packit Service c5cf8c
	else {
Packit Service c5cf8c
	    $dirname  = "";
Packit Service c5cf8c
	    $basename = $file;
Packit Service c5cf8c
	}
Packit Service c5cf8c
Packit Service c5cf8c
	# Clean up target
Packit Service c5cf8c
	if ($annoteWebPrefix ne "") {
Packit Service c5cf8c
	    $target =~ s/$annoteSrcDir/$annoteWebPrefix/;
Packit Service c5cf8c
	}
Packit Service c5cf8c
	else {
Packit Service c5cf8c
	    # make the reference relative
Packit Service c5cf8c
	    $target =~ s/$annoteSrcDir\///;
Packit Service c5cf8c
	}
Packit Service c5cf8c
Packit Service c5cf8c
	# Compare dirname to curprefix; start a new table if
Packit Service c5cf8c
	# necessary
Packit Service c5cf8c
	if ($dirname ne $curprefix) {
Packit Service c5cf8c
	    if ($curprefix ne "--NONE--") {
Packit Service c5cf8c
		for (my $j=$col-1; $j<=$maxcol; $j++) {
Packit Service c5cf8c
		    print IFD "";
Packit Service c5cf8c
		}
Packit Service c5cf8c
		print IFD "\n";
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    $curprefix = $dirname;
Packit Service c5cf8c
	    print IFD "

$dirname

\n";
Packit Service c5cf8c
	    print IFD "\n";
Packit Service c5cf8c
	    $col = 1;
Packit Service c5cf8c
	}
Packit Service c5cf8c
	if ($col == 1) {
Packit Service c5cf8c
	    print IFD "\n";
Packit Service c5cf8c
	}
Packit Service c5cf8c
	my $label = $basename;
Packit Service c5cf8c
	my $origFileName = $AnnoteFilesToOrig{$names[$i]};
Packit Service c5cf8c
	my $missed;
Packit Service c5cf8c
	my $total;
Packit Service c5cf8c
	if (defined($AnnoteMissed{$origFileName})) {
Packit Service c5cf8c
	    ($missed,$total) = split(/;/,$AnnoteMissed{$origFileName});
Packit Service c5cf8c
	    $label = "$label ($missed)";
Packit Service c5cf8c
	}
Packit Service c5cf8c
	# FIXME: 
Packit Service c5cf8c
	# Allow relative rather than absolute targets
Packit Service c5cf8c
	print IFD "$label\n";
Packit Service c5cf8c
	if ($col++ == $maxcol) {
Packit Service c5cf8c
	    print IFD "\n";
Packit Service c5cf8c
	    $col = 1;
Packit Service c5cf8c
	}
Packit Service c5cf8c
	if (defined($FilesByMissed{$missed})) {
Packit Service c5cf8c
	    $FilesByMissed{$missed} .= ";$dirname/$basename($target)";
Packit Service c5cf8c
	}
Packit Service c5cf8c
	else {
Packit Service c5cf8c
	    $FilesByMissed{$missed} = "$dirname/$basename($target)";
Packit Service c5cf8c
	}
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    # Flush the final table
Packit Service c5cf8c
    if ($curprefix ne "--NONE--") {
Packit Service c5cf8c
	# In case the page is empty
Packit Service c5cf8c
	if ($col > 1) {
Packit Service c5cf8c
	    for (my $i=$col-1; $i<=$maxcol; $i++) {
Packit Service c5cf8c
		print IFD "";
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    print IFD "\n";
Packit Service c5cf8c
	}
Packit Service c5cf8c
	print IFD "\n";
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    print IFD "


\n

Files with the most coverage gaps

\n";
Packit Service c5cf8c
    foreach my $key (sort {$b <=> $a} keys(%FilesByMissed)) {
Packit Service c5cf8c
	if ($key < 10) { last; }
Packit Service c5cf8c
	print IFD "($key): ";
Packit Service c5cf8c
	foreach my $file (split(/;/,$FilesByMissed{$key})) {
Packit Service c5cf8c
	    if ($file =~ /(.*)\((.*)\)/) {
Packit Service c5cf8c
		print IFD "$1\n";
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    else {
Packit Service c5cf8c
		print IFD "$file\n";
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	}
Packit Service c5cf8c
	print IFD "
\n";
Packit Service c5cf8c
    }
Packit Service c5cf8c
Packit Service c5cf8c
    print IFD "


\nGenerated on $date\n";
Packit Service c5cf8c
    print IFD "</BODY></HTML>\n";
Packit Service c5cf8c
    close IFD;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
# -
Packit Service c5cf8c
# ( $coveredindexfile );
Packit Service c5cf8c
# This needs to be updated
Packit Service c5cf8c
sub WriteCoveredFileMap {
Packit Service c5cf8c
    my $filename = $_[0];
Packit Service c5cf8c
    my $newline = "\r\n";
Packit Service c5cf8c
Packit Service c5cf8c
    open ( IFD, ">$filename" ) || die "Cannot open $filename\n";
Packit Service c5cf8c
Packit Service c5cf8c
    print IFD "<HTML><HEAD>$newline";
Packit Service c5cf8c
    print IFD "<TITLE>List of fully covered files</TITLE>$newline";
Packit Service c5cf8c
    print IFD "</HEAD>$newline";
Packit Service c5cf8c
    print IFD "<BODY BGCOLOR=\"FFFFFF\">$newline";
Packit Service c5cf8c
    @sortedfiles = sort ( @CoveredFiles );
Packit Service c5cf8c
#     for (my $i = 0; $i <= $#sortedfiles; $i ++ ) {
Packit Service c5cf8c
# 	my $file = $sortedfiles[$i];
Packit Service c5cf8c
# 	print IFD "$file\n";
Packit Service c5cf8c
#     }
Packit Service c5cf8c
    # This both avoids calling OutputFileTable if the array is empty
Packit Service c5cf8c
    # and keeps Perl from issuing a warning about sortedfiles having 
Packit Service c5cf8c
    # only one use.
Packit Service c5cf8c
    if ($#sortedfiles >= 0) {
Packit Service c5cf8c
        &OutputFileTable( IFD, "sortedfiles", "" );
Packit Service c5cf8c
    }
Packit Service c5cf8c
    print IFD "</BODY></HTML>$newline";
Packit Service c5cf8c
    close( IFD );
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
# -
Packit Service c5cf8c
# Make all of the directories in filename (which may include a 
Packit Service c5cf8c
# final file).  If it contains only directories, make sure that 
Packit Service c5cf8c
# the name ends in a /
Packit Service c5cf8c
sub MakeDirs {
Packit Service c5cf8c
    my $filename = $_[0];
Packit Service c5cf8c
    
Packit Service c5cf8c
    my @subdirs = split(/\//, $filename );
Packit Service c5cf8c
    print STDERR "Size of subdirs is $#subdirs\n" if $gDebugDetail;
Packit Service c5cf8c
    my $curdir = $subdirs[0];
Packit Service c5cf8c
    if ($curdir eq "") { $curdir = "/"; }
Packit Service c5cf8c
    my $rc = 0;
Packit Service c5cf8c
    for($i=1; $i<=$#subdirs; $i++) {
Packit Service c5cf8c
	print STDERR "Making $curdir\n" if $gDebugDetail;
Packit Service c5cf8c
	if (! -d $curdir) {
Packit Service c5cf8c
	    $rc = mkdir $curdir;
Packit Service c5cf8c
	    if (!$rc) { 
Packit Service c5cf8c
		print STDERR "Could not make directory $curdir\n";
Packit Service c5cf8c
		return $rc;
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	}
Packit Service c5cf8c
	if (! ($curdir =~ /\/$/)) { $curdir .= "/"; }
Packit Service c5cf8c
	$curdir .= "$subdirs[$i]";
Packit Service c5cf8c
    }
Packit Service c5cf8c
    return 1;
Packit Service c5cf8c
}
Packit Service c5cf8c
# Get all of the .gcov files from the named directory, including any subdirs
Packit Service c5cf8c
# If there is a "merge" version of the gcov file, prefer that.  These are
Packit Service c5cf8c
# used when the same source file is compiled for both the MPI and PMPI 
Packit Service c5cf8c
# interfaces, 
Packit Service c5cf8c
sub ExpandDir {
Packit Service c5cf8c
    my $dir = $_[0];
Packit Service c5cf8c
    my @otherdirs = ();
Packit Service c5cf8c
    my @files = ();
Packit Service c5cf8c
    opendir DIR, "$dir";
Packit Service c5cf8c
    for $filename (sort readdir DIR) {
Packit Service c5cf8c
	if ($filename =~ /^\./ || $filename eq ".svn") {
Packit Service c5cf8c
	    next;
Packit Service c5cf8c
	}
Packit Service c5cf8c
	elsif (-d "$dir/$filename") {
Packit Service c5cf8c
	    # Skip pmpi directories used for merging gcov output
Packit Service c5cf8c
	    if ($filename =~ /\-pmpi$/) { next; }
Packit Service c5cf8c
	    # Skip mpi directories used for handling name-mangled files
Packit Service c5cf8c
	    if ($filename =~ /\-mpi$/) { next; }
Packit Service c5cf8c
	    $otherdirs[$#otherdirs+1] = "$dir/$filename";
Packit Service c5cf8c
	}
Packit Service c5cf8c
	elsif ($filename =~ /(.*\.gcov)$/) {
Packit Service c5cf8c
	    # Check for the presense of a "merged" gcov file and use instead
Packit Service c5cf8c
	    if (-f "$dir/$filename.merge") {
Packit Service c5cf8c
		$files[$#files + 1] = "$dir/$filename.merge";
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    else {
Packit Service c5cf8c
		$files[$#files + 1] = "$dir/$filename";
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	}
Packit Service c5cf8c
    }
Packit Service c5cf8c
    closedir DIR;
Packit Service c5cf8c
    # (almost) tail recurse on otherdirs (we've closed the directory handle,
Packit Service c5cf8c
    # so we don't need to worry about it anymore)
Packit Service c5cf8c
    foreach $dir (@otherdirs) {
Packit Service c5cf8c
	@files = (@files, &ExpandDir( $dir ) );
Packit Service c5cf8c
    }
Packit Service c5cf8c
    return @files;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
# --------------------------------------------------------------------------
Packit Service c5cf8c
# HTMLify
Packit Service c5cf8c
# Take an input line and make it value HTML
Packit Service c5cf8c
sub HTMLify {
Packit Service c5cf8c
    my $line = $_[0];
Packit Service c5cf8c
    $line =~ s/\&/--AMP--/g;
Packit Service c5cf8c
    $line =~ s/>/>/g;
Packit Service c5cf8c
    $line =~ s/</</g;
Packit Service c5cf8c
    $line =~ s/--AMP--/&/g;
Packit Service c5cf8c
    return $line;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
#
Packit Service c5cf8c
# Output a table of file names
Packit Service c5cf8c
# OutputFileTable( FD, array-of-names, targethash )
Packit Service c5cf8c
sub OutputFileTable {
Packit Service c5cf8c
    my $IFD = $_[0];
Packit Service c5cf8c
    my $arrayname = $_[1];
Packit Service c5cf8c
    my $targethashname = $_[2];
Packit Service c5cf8c
Packit Service c5cf8c
    my $col = 1;
Packit Service c5cf8c
    my $maxcol = 4;
Packit Service c5cf8c
    my $curprefix = "--NONE--";
Packit Service c5cf8c
    for ($i=0; $i<=$#$arrayname; $i++) {
Packit Service c5cf8c
	my $file = $$arrayname[$i];
Packit Service c5cf8c
	my $target = "";
Packit Service c5cf8c
	if ($targethashname ne "") {
Packit Service c5cf8c
	    $target = $$targethashname{$file};
Packit Service c5cf8c
	}
Packit Service c5cf8c
Packit Service c5cf8c
	# Clean up filename
Packit Service c5cf8c
	$file =~ s/^\.\///;
Packit Service c5cf8c
	if ($file =~ /(.*)\/([^\/]*)/) {
Packit Service c5cf8c
	    $dirname  = $1;
Packit Service c5cf8c
	    $basename = $2;
Packit Service c5cf8c
	}
Packit Service c5cf8c
	else {
Packit Service c5cf8c
	    $dirname  = "";
Packit Service c5cf8c
	    $basename = $file;
Packit Service c5cf8c
	}
Packit Service c5cf8c
Packit Service c5cf8c
	# Clean up target
Packit Service c5cf8c
	if (defined($target) && $target ne "") {
Packit Service c5cf8c
	    if ($annoteWebPrefix ne "") {
Packit Service c5cf8c
		$target =~ s/$annoteSrcDir/$annoteWebPrefix/;
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    else {
Packit Service c5cf8c
		# make the reference relative
Packit Service c5cf8c
		$target =~ s/$annoteSrcDir\///;
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	}
Packit Service c5cf8c
Packit Service c5cf8c
	# Compare dirname to curprefix; start a new table if
Packit Service c5cf8c
	# necessary
Packit Service c5cf8c
	if ($dirname ne $curprefix) {
Packit Service c5cf8c
	    if ($curprefix ne "--NONE--") {
Packit Service c5cf8c
		for (my $j=$col-1; $j<=$maxcol; $j++) {
Packit Service c5cf8c
		    print $IFD "";
Packit Service c5cf8c
		}
Packit Service c5cf8c
		print $IFD "\n";
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    $curprefix = $dirname;
Packit Service c5cf8c
	    print $IFD "

$dirname

\n";
Packit Service c5cf8c
	    print $IFD "\n";
Packit Service c5cf8c
	    $col = 1;
Packit Service c5cf8c
	}
Packit Service c5cf8c
	if ($col == 1) {
Packit Service c5cf8c
	    print $IFD "\n";
Packit Service c5cf8c
	}
Packit Service c5cf8c
	my $label = $basename;
Packit Service c5cf8c
#	my $origFileName = $AnnoteFilesToOrig{$names[$i]};
Packit Service c5cf8c
	my $origFileName = $AnnoteFilesToOrig{$file};
Packit Service c5cf8c
	if (defined($origFileName)) {
Packit Service c5cf8c
            if (defined($AnnoteMissed{$origFileName})) {
Packit Service c5cf8c
	        my ($missed,$total) = split(/;/,$AnnoteMissed{$origFileName});
Packit Service c5cf8c
	        $label = "$label ($missed)";
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	}
Packit Service c5cf8c
	if ($target ne "") {
Packit Service c5cf8c
	    print $IFD "$label\n";
Packit Service c5cf8c
	}
Packit Service c5cf8c
	else {
Packit Service c5cf8c
	    print $IFD "$label\n";
Packit Service c5cf8c
	}
Packit Service c5cf8c
	if ($col++ == $maxcol) {
Packit Service c5cf8c
	    print $IFD "\n";
Packit Service c5cf8c
	    $col = 1;
Packit Service c5cf8c
	}
Packit Service c5cf8c
    }
Packit Service c5cf8c
    # Flush the final table
Packit Service c5cf8c
    if ($curprefix ne "--NONE--") {
Packit Service c5cf8c
	# In case the page is empty
Packit Service c5cf8c
	if ($col > 1) {
Packit Service c5cf8c
	    for (my $i=$col-1; $i<=$maxcol; $i++) {
Packit Service c5cf8c
		print $IFD "";
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    print $IFD "\n";
Packit Service c5cf8c
	}
Packit Service c5cf8c
	print $IFD "\n";
Packit Service c5cf8c
    }
Packit Service c5cf8c
}
Packit Service c5cf8c
#
Packit Service c5cf8c
# To generate a summary
Packit Service c5cf8c
# cd mpich/src 
Packit Service c5cf8c
# ~/projects/mpich/maint/getcoverage mpi/*/*.gcov mpi/romio/mpi-io/*.gcov \
Packit Service c5cf8c
# mpi/romio/adio/ad_nfs/*.gcov mpi/romio/adio/ad_ufs/*.gcov \
Packit Service c5cf8c
# util/info/*.gcov \
Packit Service c5cf8c
# mpid/ch3/src/*.gcov mpid/ch3/channels/sock/src/*.gcov > coverage.txt
Packit Service c5cf8c
Packit Service c5cf8c
# Now can use
Packit Service c5cf8c
# maint/getcoverage src/mpi src/util/info >coveragebase.txt
Packit Service c5cf8c
# maint/getcoverage src/mpid/ch3/src/*.gcov \
Packit Service c5cf8c
#  src/mpid/ch3/channels/sock/src/*.gcov > coveragempid.txt
Packit Service c5cf8c
Packit Service c5cf8c
#
Packit Service c5cf8c
# Skip over a named block (while writing it to the annotation file)
Packit Service c5cf8c
#   Usage:
Packit Service c5cf8c
#     skipBlockAndAnnotate( outfd, blockname );
Packit Service c5cf8c
#   e.g.,
Packit Service c5cf8c
#     skipBlockAndAnnotate( $outfile, "DEBUG" );
Packit Service c5cf8c
sub skipBlockAndAnnotate {
Packit Service c5cf8c
    my ($outfile,$blockName) = @_;
Packit Service c5cf8c
Packit Service c5cf8c
    my $sawend=0;
Packit Service c5cf8c
    print STDERR "Skipping BEGIN $blockName\n" if $gDebugDetail; 
Packit Service c5cf8c
    while (<FD>) {
Packit Service c5cf8c
	if (/^\s/) { print $outfile &HTMLify($_); }
Packit Service c5cf8c
	if (/^$lineLeader2\/\*\s+--END $blockName--\s+\*\// ||
Packit Service c5cf8c
	    /^$lineLeader3\/\*\s+--END $blockName--\s+\*\// ||
Packit Service c5cf8c
	    /^$lineLeader4\/\*\s+--END $blockName--\s+\*\//) {
Packit Service c5cf8c
	    $sawend = 1;
Packit Service c5cf8c
	    last; 
Packit Service c5cf8c
	}
Packit Service c5cf8c
    }
Packit Service c5cf8c
    if (!$sawend) {
Packit Service c5cf8c
	print STDERR "File $gFilename ended in --BEGIN $blockName-- block\n";
Packit Service c5cf8c
    }
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
# Skip over a named block.  Return the number of lines skipped
Packit Service c5cf8c
# Usage: 
Packit Service c5cf8c
#     skipBlock( blockname, allowNested, linecount )
Packit Service c5cf8c
# e.g.,
Packit Service c5cf8c
#     skipBlock( "DEBUG", 0, linecount );
Packit Service c5cf8c
sub skipBlock {
Packit Service c5cf8c
    my ($blockName,$allowNested,$linecount) = @_;
Packit Service c5cf8c
    my $fileline  = 0;
Packit Service c5cf8c
    my $lineincr  = 0;
Packit Service c5cf8c
Packit Service c5cf8c
    while (<FD>) {
Packit Service c5cf8c
	$lineincr++;
Packit Service c5cf8c
	if (/^\s/) { $fileline++; }
Packit Service c5cf8c
	if (! $allowNested) {
Packit Service c5cf8c
	    if (/^$lineLeader2\/\*\s+--BEGIN/ ||
Packit Service c5cf8c
		/^$lineLeader3\/\*\s+--BEGIN/ ||
Packit Service c5cf8c
		/^$lineLeader4\/\*\s+--BEGIN/) {
Packit Service c5cf8c
		my $cleanline = $_;
Packit Service c5cf8c
		$cleanline =~ s/^\s*//;
Packit Service c5cf8c
		chop $cleanline;
Packit Service c5cf8c
		my $curlinecount = $linecount + $lineincr;
Packit Service c5cf8c
		print STDERR "Possible missing END $blockName in $gFilename at $curlinecount; saw $cleanline\n";
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	}
Packit Service c5cf8c
# 	else {
Packit Service c5cf8c
# 	    if (/^\s*\/\*\s*--\s*END\s+$blockName\s*--\s*\*\/\s*/ ||
Packit Service c5cf8c
# 		/^\s*-:\s*\d+:\s*\/\*\s*--\s*END\s+$blockName\s*--\s*\*\/\s*/) {
Packit Service c5cf8c
# 		# FIXME: Why is this a mangled line?  Should some of the \s* be
Packit Service c5cf8c
# 		# \s+? (e.g., --\s+END)
Packit Service c5cf8c
# 		my $curlinecount = $linecount + $lineincr;
Packit Service c5cf8c
# 		print STDERR "Mangled line in $gFilename at $curlinecount: $_";
Packit Service c5cf8c
# 		last; 
Packit Service c5cf8c
# 	    }
Packit Service c5cf8c
# 	}
Packit Service c5cf8c
	if (/^$lineLeader2\/\*\s+--END $blockName--\s+\*\// ||
Packit Service c5cf8c
	    /^$lineLeader3\/\*\s+--END $blockName--\s+\*\// ||
Packit Service c5cf8c
	    /^$lineLeader4\/\*\s+--END $blockName--\s+\*\//) {
Packit Service c5cf8c
	    last; 
Packit Service c5cf8c
	}
Packit Service c5cf8c
	# If we match the following but not the preceeding, there are extra 
Packit Service c5cf8c
	# spaces in the comment
Packit Service c5cf8c
	if (/^\s*\/\*\s*--\s*END\s+$blockName\s*--\s*\*\/\s*/ ||
Packit Service c5cf8c
	    /^\s*-:\s*\d+:\s*\/\*\s*--\s*END\s+$blockName\s*--\s*\*\/\s*/) {
Packit Service c5cf8c
	    my $curlinecount = $linecount + $lineincr;
Packit Service c5cf8c
	    print STDERR "Mangled line in $gFilename at $curlinecount: $_";
Packit Service c5cf8c
	    last; 
Packit Service c5cf8c
	}
Packit Service c5cf8c
    }
Packit Service c5cf8c
    return ($lineincr,$fileline);
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
#
Packit Service c5cf8c
# skipIfdefBlock 
Packit Service c5cf8c
# (ToDo)