Arquivos
mpich/maint/extracterrmsgs
Hui Zhou a0798a79f9 maint/extracterrmsgs: auto generate errnames for ofi calls
Since all error messages for ofi calls are uniform, generate them
instead of manually maintaining it.

For context, the current usages have grow out-of-sync with
src/mpid/ch4/netmod/ofi/errnames.txt. The following errnames were
missing, largely due to mis-spelling:
    **ofid_atomicto
    **ofid_control
    **ofid_fabricclose
    **ofid_fi_close
    **ofid_fi_tsenddata
    **ofid_mr_bind
    **ofid_mr_enable
    **ofid_mr_regattr
    **ofid_tsendv
This commit makes sure they are always consistent.
2024-09-05 12:52:38 -05:00

975 linhas
32 KiB
Perl
Arquivo Executável

#! /usr/bin/env perl
##
## Copyright (C) by Argonne National Laboratory
## See COPYRIGHT in top-level directory
##
use strict;
# (Tested with -w; 10/5/04)
#
# Find the parse.sub routine.
my $maintdir = "./maint";
my $rootdir = ".";
if ( ! -s "maint/parse.sub" ) {
my $program = $0;
$program =~ s/extracterrmsgs//;
if (-s "$program/parse.sub") {
$maintdir = $program;
$rootdir = $program;
$rootdir =~ s/\/maint//g;
}
}
require "$maintdir/parse.sub";
my $debug = 0;
my $careful = 0; # Set careful to 1 to flag unused messages
my $carefulFilename = "";
my $showfiles = 0;
my $quiet = 0;
my $build_test_pgm = 1;
# FIXME: checkErrClass should be set to 1; currently set to zero
# to permit autogen.sh to complete
my $checkErrClass = 1;
# Strict is used to control checking of error message strings.
my $gStrict = 0;
if (defined($ENV{"DEBUG_STRICT"})) { $gStrict = 1; }
our (%generic_msgs, %generic_loc, %specific_msgs, %specific_loc);
# Hard code usages that are generated by scripts
foreach my $k ("envvarparse", "cvar_val"){
$generic_msgs{"**$k"}=1;
$specific_msgs{"**$k"}=1;
$generic_loc{"**$k"}=":src/util/mpir_cvars.c";
$specific_loc{"**$k"}=":src/util/mpir_cvars.c";
}
# Check for special args
my $mpi_h = "src/include/mpi.h.in";
my $baseerrnames_txt = "src/mpi/errhan/baseerrnames.txt";
my @files = ();
my %skipFiles = ();
my @errnameFiles = ();
my $outfile = "";
foreach my $arg (@ARGV) {
if ($arg =~ /^--?showfiles/) { $showfiles = 1; }
elsif( $arg =~ /^--?debug/) { $debug = 1; }
elsif( $arg =~ /^--?quiet/) { $quiet = 1; }
elsif( $arg =~ /^--?notest/) { $build_test_pgm = 0; }
elsif( $arg =~ /^--?outfile=(.*)/) { $outfile = $1; }
elsif( $arg =~ /^--?careful=(.*)/) {
$careful = 1;
$carefulFilename = $1;
}
elsif( $arg =~ /^--?careful/) { $careful = 1; }
elsif( $arg =~ /^--?strict/) { $gStrict = 1; }
elsif( $arg =~ /^--?skip=(.*)/) { $skipFiles{$1} = 1; }
else {
print "Adding $arg to files\n" if $debug;
if (-d $arg) {
# Add all .c files from directory $arg to the list of files
# to process (this lets us shorten the arg list)
ExpandDir(\@files, $arg);
}
else {
# errname files are treated differently
if ($arg =~ m{(^|[/\\])errnames.txt$}) {
push @errnameFiles, $arg;
}
else {
$files[$#files+1] = $arg;
}
}
}
}
# End of argument processing
print "Rootdir = $rootdir\n" if $debug;
# Setup the basic file for errnames - Now determined in ExpandDirs
#@errnameFiles = ( "$rootdir/src/mpi/errhan/errnames.txt" );
my $OUTFD;
if ($outfile ne "") {
open( $OUTFD, ">$outfile" ) or die "Could not open $outfile\n";
}
else {
$OUTFD = *STDOUT;
}
# Setup before processing the files
if ($build_test_pgm && -d "test/mpi/errhan") {
# Get current directory in case we need it for the error message
my $curdir = `pwd`;
open( TESTFD, ">test/mpi/errhan/errcode.c" ) or die "Cannot create test program errcode.c in $curdir/test/mpi/errhan\n";
print TESTFD "/*\
* Copyright (C) by Argonne National Laboratory\
* See COPYRIGHT in top-level directory\
*\
* This file is automatically generated by maint/extracterrmsgs\
* DO NOT EDIT\
*/\n";
print TESTFD "#include <stdio.h>\n#include <stdlib.h>\n#include \"mpi.h\"\n";
print TESTFD "#define MPIR_ERR_FATAL 1\n";
print TESTFD "#define MPIR_ERR_RECOVERABLE 0\n";
print TESTFD "int MPIR_Err_create_code( int, int, char *, int, int, const char [], const char [], ... );\n";
print TESTFD "void ChkMsg( int, int, const char [] );\n\n";
print TESTFD "int main(int argc, char **argv)\n";
print TESTFD "{\n int err;\n MPI_Init( 0, 0 );\n";
}
# Load mpi.h for error class constants
my %mpi_h_constants;
$mpi_h_constants{"MPI_SUCCESS"} = 0;
if (open In, $mpi_h) {
while (<In>) {
if (/^#define\s+((MPICH|MPI|MPI_T|MPIX)_ERR_\w+)\s+(.+)/) {
my ($key, $t) = ($1, $3);
if ($t=~/^(\d+)/) {
$mpi_h_constants{$key} = $1;
} elsif ($t=~/MPICH_ERR_FIRST_MPIX\s*\+\s*(\d+)/) {
$mpi_h_constants{$key} = $mpi_h_constants{MPICH_ERR_FIRST_MPIX} + $1;
}
}
}
close In;
} else {
die "Unable to read $mpi_h\n";
}
my $max_err_class = $mpi_h_constants{MPICH_ERR_LAST_MPIX};
if (!$max_err_class) {
die "Failed to load MPICH_ERR_LAST_MPIX from $mpi_h\n";
}
# Process the definitions
my (%generic_msgs, %generic_loc, %specific_msgs, %specific_loc);
foreach my $file (@files) {
print "$file\n" if $showfiles;
&ProcessFile( $file );
}
#
# Create the hash %longnames that maps the short names to the long names,
# $longnames{shortname} => longname, by reading the errnames.txt files
#
my (%longnames, %longnamesDefined);
foreach my $sourcefile (@errnameFiles) {
#print STDERR "processing $sourcefile for error names\n";
&ReadErrnamesFile( $sourcefile );
}
# Load baseerrnames.txt
my @class_msgs;
if (open In, $baseerrnames_txt) {
while (<In>) {
if (/^(MPI\w+)\s+(\*\*\w+)/) {
my ($name, $shortmsg) = ($1, $2, $3);
my $id = $mpi_h_constants{$name};
if (defined $id) {
$generic_msgs{$shortmsg}++;
$generic_loc{$shortmsg} = ":baseerrnames.txt";
$class_msgs[$id] = $shortmsg;
} else {
die "error class $name not found in mpi.h\n";
}
}
}
close In;
} else {
die "Unable to read $baseerrnames_txt\n";
}
# Create the output files from the input that we've read
my (%longnamesUsed, %short_to_num);
&CreateErrmsgsHeader( $OUTFD );
&CreateErrMsgMapping( $OUTFD );
if ($build_test_pgm && -d "test/mpi/errhan") {
print TESTFD " MPI_Finalize();\n return 0;\n}\n";
close TESTFD;
}
#
# Generate a list of unused keys
if ($careful) {
my $OUTFD = *STDERR;
if ($carefulFilename ne "") {
open $OUTFD, ">$carefulFilename" or die "Cannot open $carefulFilename";
}
foreach my $shortname (keys(%longnames)) {
if (!defined($longnamesUsed{$shortname}) ||
$longnamesUsed{$shortname} < 1) {
my $loc = $longnamesDefined{$shortname};
print $OUTFD "Name $shortname is defined in $loc but never used\n";
}
}
if ($carefulFilename ne "") {
close $OUTFD;
}
}
#-----------------------------------------------------------------------------
# ROUTINES
# ----------------------------------------------------------------------------
# From the data collected above, generate the file containing the error message
# text.
# This is a temporary routine; the exact output form will be defined later
sub CreateErrmsgsHeader {
my $FD = $_[0];
print $FD "/*\
* Copyright (C) by Argonne National Laboratory\
* See COPYRIGHT in top-level directory\
*\
* This file automatically created by extracterrmsgs\
* DO NOT EDIT\
*/\n";
print $FD "
typedef struct msgpair {
const unsigned int sentinal1;
const char *short_name, *long_name;
const unsigned int sentinal2; } msgpair;
\n"
}
#
# We also need a way to create the records
# We then hash these on the first occurrence (or precompute the hashes?)
#
# The error messages are output in the following form:
# typedef struct {const char short[], const long[]} namemap;
# Generic messages
# static const char[] short1 = "";
# static const char[] long1 = "";
# ...
# static const namemap[] = { {short1, long1}, {...} }
#
sub CreateErrMsgMapping {
my $OUTFD = $_[0];
# Now, output each short,long key
# Do the generic, followed by the specific, messages
# The long messages must be available for the generic message output.
# An alternative is to separate the short from the long messages;
# the long messages are needed for > MSG_NONE, the short for > MSG_CLASS.
print $OUTFD "/* The names are in sorted order, allowing the use of a simple\
linear search or bisection algorithm to find the message corresponding to\
a particular message */\n";
my @sorted_generic_msgs = sort keys %generic_msgs;
# add a dummy UNKNOWN entry in the front.
# NOTE: assume all other generic message are lowercase so "UNKNOWN" will
# be ordered first. This is critical because FindGenericMsgIndex assumes
# this ordering!
unshift @sorted_generic_msgs, "**UNKNOWN";
$generic_loc{"**UNKNOWN"} = ":[NONE]";
$longnames{"**UNKNOWN"} = "Unknown error class";
my $num = 0;
foreach my $key (@sorted_generic_msgs)
{
my $longvalue = "\"\0\"";
if (!defined($longnames{$key}))
{
my $seenfile = $generic_loc{$key};
if ($key =~ /^\*\*/) {
# If the message begins with text, assume that it is a
# literal message
print STDERR "Shortname $key for generic messages has no expansion (first seen in file $seenfile)\n";
}
next;
}
else {
# Keep track of which messages we have seen
$longnamesUsed{$key} += 1;
}
# Escape any naked quotes (This should be applied somewhere else?)
# $longvalue = s/(?<!\\)\"/\\\"/;
$longvalue = "\"" . $longnames{$key} . "\"";
print $OUTFD "static const char short_gen$num\[\] = \"$key\";\n";
# print $OUTFD "static const char short_gen$num\[\] = $key;\n";
print $OUTFD "static const char long_gen$num\[\] = $longvalue;\n";
# Remember the number assigned to this short string.
$short_to_num{$key} = $num;
$num ++;
}
# Generate the mapping of short to long names
print $OUTFD "\nstatic const int generic_msgs_len = $num;\n";
# The sentinels should be hardcoded into the source file that
# uses this file to ensure that the sentinel tests are ok.
my $sentinal1 = "0xacebad03";
my $sentinal2 = "0xcb0bfa11";
print $OUTFD "static const msgpair generic_err_msgs[] = {\n";
for (my $i = 0; $i < $num; $i ++) {
print $OUTFD " { $sentinal1, short_gen$i, long_gen$i, $sentinal2 }";
print $OUTFD "," if ($i < $num - 1);
print $OUTFD "\n";
}
print $OUTFD "};\n";
$num = 0;
# Now output the instance specific messages
foreach my $key (sort keys %specific_msgs)
{
my $longvalue = "\"\0\"";
if (!defined($longnames{$key}))
{
print STDERR "Shortname $key for specific messages has no expansion (first seen in file $specific_loc{$key})\n";
next;
}
else {
# Keep track of which messages we have seen
$longnamesUsed{$key} += 1;
}
# Escape any naked quotes
$longvalue =~ s/(?<!\\)\"/\\\"/;
$longvalue = "\"" . $longnames{$key} . "\"";
print $OUTFD "static const char short_spc$num\[\] = \"$key\";\n";
# print $OUTFD "static const char short_spc$num\[\] = $key;\n";
print $OUTFD "static const char long_spc$num\[\] = $longvalue;\n";
$num ++;
}
# Generate the mapping of short to long names
print $OUTFD "\nstatic const int specific_msgs_len = $num;\n";
print $OUTFD "static const msgpair specific_err_msgs[] = {\n";
for (my $i = 0; $i < $num ; $i ++) {
print $OUTFD " { $sentinal1, short_spc$i, long_spc$i, $sentinal2 }";
print $OUTFD "," if ($i < $num - 1);
print $OUTFD "\n";
}
print $OUTFD "};\n";
my $maxval = $max_err_class + 1;
print $OUTFD "static int class_to_index[] = {\n ";
for (my $i=0; $i<=$max_err_class; $i++) {
my $idx = $short_to_num{$class_msgs[$i]};
if (!$idx) {
# 0 is the "**UNKNOWN" entry
$idx = 0;
}
print $OUTFD "$idx";
print $OUTFD "," if ($i < $max_err_class);
print $OUTFD "\n " if !(($i + 1) % 10);
}
print $OUTFD "\n};\n";
}
#
# Add a call to test this message for the error message.
# Handle both the generic and specific messages
#
my (%test_generic_msg, %test_specific_msg);
sub AddTestCall {
my ($filename, $genericArgLoc, @msg_args) = @_;
my $last_errcode = "MPI_SUCCESS"; # $_[0];
my $fatal_flag = "MPIR_ERR_RECOVERABLE"; # $_[1];
my $fcname = "unknown"; # $_[2];
my $linenum = "__LINE__"; # $_[3];
my $errclass = "MPI_ERR_OTHER"; # $_[4];
my $generic_msg = $msg_args[$genericArgLoc];
my $specific_msg = $msg_args[$genericArgLoc+1];
if (!defined $specific_msg) {
$specific_msg = "0";
}
# Ensure that the last_errcode, class and fatal flag are specified. There are a few places where these are variables.
if (!($last_errcode =~ /MPI_ERR_/) )
{
$last_errcode = "MPI_SUCCESS";
}
if (!($errclass =~ /MPI_ERR_/) )
{
$errclass = "MPI_ERR_OTHER";
}
if (!($fatal_flag =~ /MPIR_ERR_FATAL/) && !($fatal_flag =~ /MPIR_ERR_RECOVERABLE/))
{
$fatal_flag = "MPIR_ERR_FATAL";
}
# Generic message (first instance only)
if (!defined($test_generic_msg{$generic_msg}))
{
$test_generic_msg{$generic_msg} = $filename;
print TESTFD " /* $filename */\n";
print TESTFD " err = MPIR_Err_create_code($last_errcode, $fatal_flag, \"errcode::main\", __LINE__, $errclass, ".
"$generic_msg, 0);\n";
print TESTFD " ChkMsg( err, $errclass, $generic_msg );\n";
}
# Specific messages
$specific_msg = "0" if ($specific_msg =~ /^\s*NULL\s*$/);
if ($specific_msg ne "0" && !defined($test_specific_msg{$specific_msg}))
{
$test_specific_msg{$specific_msg} = $filename;
print TESTFD " {\n";
print TESTFD " /* $filename */\n";
# Use types in the string to create the types with default
# values
my $format = $specific_msg;
my $fullformat = $format;
my $narg = 0;
my @args = ();
while ($format =~ /[^%]*%(.)(.*)/)
{
my $type = $1;
$format = $2;
$narg ++;
if ($type eq "d")
{
print TESTFD " int i$narg = $narg;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "x")
{
print TESTFD " int i$narg = $narg;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "L")
{
print TESTFD " long long i$narg = $narg;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "X")
{
print TESTFD " long long i$narg = $narg;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "i")
{
print TESTFD " int i$narg = $narg;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "t")
{
print TESTFD " int i$narg = $narg;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "s")
{
print TESTFD " char s$narg\[\] = \"string$narg\";\n";
$args[$#args+1] = "s$narg";
}
elsif ($type eq "p")
{
print TESTFD " char s$narg\[\] = \"pointer$narg\";\n";
$args[$#args+1] = "s$narg";
}
elsif ($type eq "C")
{
print TESTFD " int i$narg = MPI_COMM_WORLD;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "I")
{
print TESTFD " int i$narg = MPI_INFO_NULL;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "D")
{
print TESTFD " int i$narg = MPI_INT;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "F")
{
# This must be an MPI_File since that type may not
# be an integer (it is a pointer at this time)
print TESTFD " MPI_File i$narg = MPI_FILE_NULL;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "W")
{
print TESTFD " int i$narg = MPI_WIN_NULL;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "A")
{
print TESTFD " int i$narg = $narg;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "G")
{
print TESTFD " int i$narg = MPI_GROUP_NULL;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "O")
{
print TESTFD " int i$narg = MPI_SUM;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "R")
{
print TESTFD " int i$narg = MPI_REQUEST_NULL;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "E")
{
print TESTFD " int i$narg = MPI_ERRORS_RETURN;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "K")
{
print TESTFD " int i$narg = MPI_KEYVAL_INVALID;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "S")
{
print TESTFD " int i$narg = MPI_SESSION_NULL;\n";
$args[$#args+1] = "i$narg";
}
elsif ($type eq "c")
{
print TESTFD " MPI_Count i$narg = $narg;\n";
$args[$#args+1] = "i$narg";
}
else
{
print STDERR "Unrecognized format type $type for $fullformat in $filename\n";
}
}
my $actargs = $#msg_args - $genericArgLoc - 1;
if ($actargs != $narg)
{
print STDERR "Error: Format $fullformat provides $narg arguments but call has $actargs in $filename\n";
}
print TESTFD " err = MPIR_Err_create_code($last_errcode, $fatal_flag, \"errcode::main\", __LINE__, $errclass, " .
"$generic_msg, $specific_msg";
foreach my $arg (@args)
{
print TESTFD ", $arg";
}
print TESTFD " );\n";
print TESTFD " ChkMsg( err, $errclass, $specific_msg );\n }\n";
# ToDo: pass another string to ChkMsg that contains the
# names of the variables, as a single string (comma separated).
# This allows us to review the source of the values for the args.
}
}
# ==========================================================================
# Read an errnames file. This allows us to distribute the errnames.txt
# files in the relevant modules, rather than making them part of one
# single main file.
# This updates the global hashes longnames and longnamesDefined
# ReadErrnamesFile( filename )
# ==========================================================================
sub ReadErrnamesFile {
my $sourcefile = $_[0];
open( FD, "<$sourcefile" ) or return 0;
my $linecount = 0;
while (<FD>) {
$linecount++;
# Skip Comments
if (/^\s*\#/) { next; }
# Read entire error message (allow \ at end of line to continue)
if (/^\s*(\*\*[^:]*):(.*)$/) {
my $name = $1;
my $repl = $2;
$repl =~ s/\r*\n*$//g;
while ($repl =~ /\\\s*$/) {
# If there is a \\ at the end, read another.
# Remove the \ at the end (an alternative is to turn
# it into a \n (newline), but we may want to avoid
# multiline messages
$repl =~ s/\\\s*$//;
my $inline = <FD>;
$linecount++;
$inline =~ s/^\s*//; # remove leading spaces
$repl .= $inline;
$repl =~ s/[\r\n]*$//g; # remove newlines
}
# Check that the name and the replacement text at least
# partially match as to format specifiers
# (They should have exactly the same pattern, i.e.,
# if the name has %d %x in is, the replacement should
# have %d %x, in that order)
my $namehasformat = ($name =~ /%/);
my $replhasformat = ($repl =~ /%/);
if ($namehasformat != $replhasformat) {
print STDERR "Warning: format control usage in $name and $repl do not agree in $sourcefile\n";
}
# if (!defined($longnames{"\"$name\""}))
# {
# $longnames{"\"$name\""} = $repl;
# $longnamesDefined{"\"$name\""} = "$sourcefile:$linecount";
# }
# Check that the replacement text doesn't include a unquoted
# double quote
if ($repl =~ /(.)\"/) {
my $prechar = $1;
if ($1 ne "\\") {
print STDERR "Warning: Replacement text for $name contains an unescaped double quote: $repl\n";
}
}
if (!defined($longnames{$name}))
{
$longnames{$name} = $repl;
$longnamesDefined{$name} = "$sourcefile:$linecount";
}
else
{
print STDERR "Warning: attempt to redefine $name. Duplicate ignored.\n";
print STDERR "Previously defined at $longnamesDefined{$name} with value \"$longnames{$name}\"\n";
print STDERR "Redefined at $sourcefile:$linecount with value \"$repl\"\n";
}
}
}
close( FD );
}
# ==========================================================================
# Call this for each file
# This reads a C source or header file and adds does the following:
# adds any generic message short names encountered to the hash generic_msgs.
# adds any specific message short names encounter to the hash specific_msgs.
# adds the filename to the hash generic_loc{msg} as the value (: separated)
# and the same for hash specific_loc{msg}.
# The last two are used to provide better error reporting.
#
my %KnownErrRoutines;
my %bad_syntax_in_file;
sub ProcessFile
{
if (!%KnownErrRoutines) {
load_KnownErrRoutines();
}
my $filename = $_[0];
my $linecount = 0;
my $remainder;
open (my $FD, "<$filename" ) or die "Could not open $filename\n";
while (<$FD>) {
$linecount++;
# Skip code that is marked as ignore (e.g., for
# macros that are used to simplify the use of MPIR_Err_create_code
# (such macros must also be recognized and processed)
if (/\/\*\s+--BEGIN ERROR MACROS--\s+\*\//) {
while (<$FD>) {
$linecount++;
if (/\/\*\s+--END ERROR MACROS--\s+\*\//) { last; }
}
$remainder = "";
next;
}
# Next, remove any comments
$_ = StripComments($FD, $_ );
# Skip the definition of the function
if (/int\s+MPI[OUR]_Err_create_code/) { $remainder = ""; next; }
if (/^\s*MPIDI_OFI_(CALL|CALL_RETRY|CALL_RETRY_AM|VCI_CALL)\b(.*)/) {
my $arglist = $2;
my ($leader, @args);
($leader, $remainder, @args ) = &GetSubArgs($FD, $arglist );
my $name = $args[-1];
if (!$generic_msgs{"**ofid_$name"}) {
# add longnames since we omit errnames.txt for these
$longnames{"**ofid_$name"} = "OFI call $name failed";
$longnamesDefined{"**ofid_$name"} = "$filename:$linecount";
$longnames{"**ofid_$name %s %d %s %s"} = "OFI call $name failed (%s:%d:%s:%s)";
$longnamesDefined{"**ofid_$name %s %d %s %s"} = "$filename:$linecount";
}
$generic_msgs{"**ofid_$name"}++;
$specific_msgs{"**ofid_$name %s %d %s %s"}++;
next;
}
while (/(MPI[OUR]_Err[A-Za-z0-9_]+)\s*(\(.*)$/i) {
my $routineName = $1;
my $arglist = $2;
if ($routineName =~ /MPIR_ERR_(CHECK|POP|ADD|GET_CLASS|COLL_CHECKANDCONT|is_fatal)/i) {
# skip known false positives
next;
}
if (!defined($KnownErrRoutines{$routineName})) {
print "Skipping $routineName\n" if $debug;
last;
}
print "Found $routineName\n" if $debug;
my ($genericArgLoc,$hasLine,$hasSpecific,$onlyIndirect,$errClassLoc) =
split(/:/,$KnownErrRoutines{$routineName});
my ($leader, @args);
($leader, $remainder, @args ) = &GetSubArgs($FD, $arglist );
# Discard leader
if ($debug) {
print "Line begins with $leader\n"; # Use $leader to keep -w happy
foreach my $arg (@args) {
print "|$arg|\n";
}
}
# Process the signature
# if signature does not match new function prototype, then skip it
if ($#args < $genericArgLoc) {
if (!defined($bad_syntax_in_file{$filename})) {
$bad_syntax_in_file{$filename} = 1;
print STDERR "Warning: $routineName call with too few arguments in $filename\n";
}
next;
}
if ($hasLine >= 0 &&
($args[$hasLine] ne "__LINE__" && $args[$hasLine] ne "line")) {
if (!defined($bad_syntax_in_file{$filename})) {
$bad_syntax_in_file{$filename} = 1;
my $tmpi = $hasLine + 1;
print STDERR "Warning: Expected __LINE__ or line as ${tmpi}th argument of $routineName in $filename\n";
}
next;
}
if ($errClassLoc >= 0 && $checkErrClass) {
if (!($args[$errClassLoc] =~ /^MPI_ERR_/) &&
!($args[$errClassLoc] =~ /^MPI_T_ERR_/) &&
!($args[$errClassLoc] =~ /^MPIDI_CH3I_SOCK_ERR_/) &&
!($args[$errClassLoc] =~ /^MPIX_ERR_/) &&
!($args[$errClassLoc] =~ /^errclass/) &&
!($args[$errClassLoc] =~ /^errflag/) &&
!($args[$errClassLoc] =~ /^\*errflag/)) {
$bad_syntax_in_file{$filename} = 1;
print STDERR "Invalid argument $args[$errClassLoc] for the MPI Error class in $routineName in $filename\n";
next;
}
}
#my $last_errcode = $args[0];
#my $fatal_flag = $args[1];
#my $fcname = $args[2];
#my $linenum = $args[3];
#my $errclass = $args[4];
my $generic_msg = $args[$genericArgLoc];
my $specific_msg = "0";
if ($hasSpecific) {
$specific_msg = $args[$genericArgLoc+1];
}
# Check the generic and specific message arguments
if ($generic_msg =~ /\s$/)
{
print STDERR "Warning: trailing blank on arg $generic_msg in $filename!\n";
}
if ($onlyIndirect && !($generic_msg =~ /^\"\*\*\S+\"$/)) {
print STDERR "Error: generic message $generic_msg has incorrect format in $filename\n";
next;
}
if ($generic_msg =~ /%/) {
print STDERR "Warning: generic message $generic_msg in $filename contains a format control\n";
}
$specific_msg = "0" if ($specific_msg =~ /^\s*NULL\s*$/);
if ($specific_msg =~ /^[1-9]/)
{
print STDERR "Error: instance specific message $specific_msg in $filename is an invalid integer ".
"(must be 0 or a string)\n";
next;
}
if ($specific_msg eq $generic_msg)
{
print STDERR "Warning: generic and instance specific messages must be different " .
"(file $filename, message $generic_msg)\n";
}
if ($specific_msg ne "0" && !($specific_msg =~ /\%/))
{
print STDERR "Warning: instance specific message $specific_msg in $filename contains no format control\n";
}
if ($specific_msg =~ /%/) {
# Specific message includes format values. Check
# for number and for valid strings if %s
my $nargs = 0;
my $tmpmsg = $specific_msg;
my @stringLocs = ();
while ($tmpmsg =~ /[^%]*%(.)(.*)/) {
$tmpmsg = $2;
my $followchar = $1;
if ($followchar eq "s") {
$stringLocs[$#stringLocs+1] = $nargs;
}
if ($followchar ne "%") {
$nargs ++;
}
if (! ($followchar =~ /[%sdxitpcDCRWOEIGFALXKS]/) ) {
print STDERR "Warning: Unrecognized format specifier in specific message $specific_msg in $filename\n";
}
}
if ($nargs != $#args - $genericArgLoc - 1) {
my $actargs = $#args - $genericArgLoc - 1;
print STDERR "Warning: wrong number of arguments for instance specific message $specific_msg in $filename; expected $nargs but found $actargs\n";
}
elsif ($#stringLocs >= 0 && $gStrict) {
# Check for reasonable strings if strict checking requested
for (my $i=0; $i<=$#stringLocs; $i++) {
my $index = $stringLocs[$i];
my $string = $args[$genericArgLoc+2+$index];
if ($string =~ /\"/) {
# Allow a few special cases:
# Always: all uppercase and _, single word
my $stringOk = 0;
if ($string =~ /^\"[A-Z_]*\"$/) {
$stringOk = 1;
}
elsif ($string =~ /^\"\w*\"$/) {
if (1) { $stringOk = 1; }
}
if (!$stringOk) {
print STDERR "Warning: explicit string as argument to specific message $specific_msg in $filename; explicit string is $string\n";
}
}
}
}
}
if ($build_test_pgm) {
&AddTestCall($filename, $genericArgLoc, @args )
}
if ($generic_msg =~ /^\"(.*)\"$/) {
$generic_msg = $1;
$generic_msgs{$generic_msg}++;
$generic_loc{$generic_msg} .= ":$filename";
}
else {
$generic_msgs{$generic_msg}++;
$generic_loc{$generic_msg} .= ":$filename";
}
if ($specific_msg =~ /^\"(\*\*.*)\"/)
{
$specific_msg = $1;
$specific_msgs{$specific_msg}++;
$specific_loc{$specific_msg} .= ":$filename";
}
}
continue
{
$_ = $remainder;
}
}
close $FD;
}
# Get all of the .c files from the named directory, including any subdirs
# Also, add any errnames.txt files to the errnamesFiles arrays
sub ExpandDir {
my ($files, $dir) = @_;
my @otherdirs = ();
opendir DIR, "$dir";
while (my $filename = readdir DIR) {
if ($filename =~ /^\./) {
next;
}
elsif (-d "$dir/$filename") {
$otherdirs[$#otherdirs+1] = "$dir/$filename";
}
elsif ($filename =~ /(.*\.[chi])(pp){0,1}$/) {
# Test for both Unix- and Windows-style directory separators
if (!defined($skipFiles{"$dir/$filename"}) &&
!defined($skipFiles{"$dir\\$filename"})) {
push @$files, "$dir/$filename";
}
}
elsif ($filename eq "errnames.txt") {
$errnameFiles[$#errnameFiles+1] = "$dir/$filename";
}
}
closedir DIR;
# (almost) tail recurse on otherdirs (we've closed the directory handle,
# so we don't need to worry about it anymore)
foreach my $dir (@otherdirs) {
ExpandDir($files, $dir);
}
}
sub load_KnownErrRoutines {
# Match the known routines and macros.
# Then check that the arguments match if there is a
# specific string (number of args matches the number present)
# MPIR_ERR_CHK(FATAL)?ANDJUMP[1-4]?(cond,code,class,gmsg[,smsg,args])
# MPIR_ERR_SET(FATAL)?ANDJUMP[1-4]?(code,class,gmsg[,smsg,args])
# MPIR_ERR_CHK(FATAL)?ANDSTMT[1-4]?(cond,code,class,stmt,gmsg[,smsg,args])
# MPIR_ERR_SET(FATAL)?ANDSTMT[1-4]?(code,class,stmt,gmsg[,smsg,args])
# Value is a tuple of:
# the count of args where the generic msg begins (starting from 0)
# location of __LINE__ (-1 for none)
# specific msg arg required (0 for no, > 0 for yes)
# is the generic message an indirect from errnames.txt (1=yes 0=no)
# location of the error class
%KnownErrRoutines = (
'MPIR_Err_create_code' => '5:3:1:1:4',
'MPIO_Err_create_code' => '5:3:1:0:-1',
'MPIR_ERR_SET' => '2:-1:0:1:1',
'MPIR_ERR_SETSIMPLE' => '2:-1:0:1:1',
'MPIR_ERR_SET1' => '2:-1:1:1:1',
'MPIR_ERR_SET2' => '2:-1:2:1:1',
'MPIR_ERR_SETANDSTMT' => '3:-1:0:1:1',
'MPIR_ERR_SETANDSTMT1' => '3:-1:1:1:1',
'MPIR_ERR_SETANDSTMT2' => '3:-1:1:1:1',
'MPIR_ERR_SETANDSTMT3' => '3:-1:1:1:1',
'MPIR_ERR_SETANDSTMT4' => '3:-1:1:1:1',
'MPIR_ERR_SETANDJUMP' => '2:-1:0:1:1',
'MPIR_ERR_SETANDJUMP1' => '2:-1:1:1:1',
'MPIR_ERR_SETANDJUMP2' => '2:-1:1:1:1',
'MPIR_ERR_SETANDJUMP3' => '2:-1:1:1:1',
'MPIR_ERR_SETANDJUMP4' => '2:-1:1:1:1',
'MPIR_ERR_CHKANDSTMT' => '4:-1:0:1:2',
'MPIR_ERR_CHKANDSTMT1' => '4:-1:1:1:2',
'MPIR_ERR_CHKANDSTMT2' => '4:-1:1:1:2',
'MPIR_ERR_CHKANDSTMT3' => '4:-1:1:1:2',
'MPIR_ERR_CHKANDSTMT4' => '4:-1:1:1:2',
'MPIR_ERR_CHKANDJUMP' => '3:-1:0:1:2',
'MPIR_ERR_CHKANDJUMP1' => '3:-1:1:1:2',
'MPIR_ERR_CHKANDJUMP2' => '3:-1:1:1:2',
'MPIR_ERR_CHKANDJUMP3' => '3:-1:1:1:2',
'MPIR_ERR_CHKANDJUMP4' => '3:-1:1:1:2',
'MPIR_ERR_SETFATAL' => '2:-1:0:1:1',
'MPIR_ERR_SETFATALSIMPLE' => '2:-1:0:1:1',
'MPIR_ERR_SETFATAL1' => '2:-1:1:1:1',
'MPIR_ERR_SETFATAL2' => '2:-1:2:1:1',
'MPIR_ERR_SETFATALANDSTMT' => '3:-1:0:1:1',
'MPIR_ERR_SETFATALANDSTMT1' => '3:-1:1:1:1',
'MPIR_ERR_SETFATALANDSTMT2' => '3:-1:1:1:1',
'MPIR_ERR_SETFATALANDSTMT3' => '3:-1:1:1:1',
'MPIR_ERR_SETFATALANDSTMT4' => '3:-1:1:1:1',
'MPIR_ERR_SETFATALANDJUMP' => '2:-1:0:1:1',
'MPIR_ERR_SETFATALANDJUMP1' => '2:-1:1:1:1',
'MPIR_ERR_SETFATALANDJUMP2' => '2:-1:1:1:1',
'MPIR_ERR_SETFATALANDJUMP3' => '2:-1:1:1:1',
'MPIR_ERR_SETFATALANDJUMP4' => '2:-1:1:1:1',
'MPIR_ERR_CHKFATALANDSTMT' => '4:-1:0:1:2',
'MPIR_ERR_CHKFATALANDSTMT1' => '4:-1:1:1:2',
'MPIR_ERR_CHKFATALANDSTMT2' => '4:-1:1:1:2',
'MPIR_ERR_CHKFATALANDSTMT3' => '4:-1:1:1:2',
'MPIR_ERR_CHKFATALANDSTMT4' => '4:-1:1:1:2',
'MPIR_ERR_CHKFATALANDJUMP' => '3:-1:0:1:2',
'MPIR_ERR_CHKFATALANDJUMP1' => '3:-1:1:1:2',
'MPIR_ERR_CHKFATALANDJUMP2' => '3:-1:1:1:2',
'MPIR_ERR_CHKFATALANDJUMP3' => '3:-1:1:1:2',
'MPIR_ERR_CHKFATALANDJUMP4' => '3:-1:1:1:2',
'MPIR_ERRTEST_VALID_HANDLE' => '4:-1:0:1:3',
);
}
#
# Other todos:
# It would be good to keep track of any .N MPI_ERR_xxx names in the structured
# comment and match these against any MPI_ERR_yyy used in the code, emitting a
# warning message for MPI_ERR_yyy values used in the code but not mentioned
# in the header. This could even apply to routines that are not at the MPI
# layer, forcing all routines to document all MPI error classes that they might
# return (this is like requiring routines to document the exceptions that
# they may throw).