mirror of
https://github.com/ioacademy-jikim/debugging
synced 2025-06-07 16:06:09 +00:00
2797 lines
100 KiB
C
2797 lines
100 KiB
C
|
|
/*---------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- A library of wrappers for MPI 2 functions. ---*/
|
|
/*--- ---*/
|
|
/*---------------------------------------------------------------*/
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
Notice that the following BSD-style license applies to this one
|
|
file (mpiwrap.c) only. The rest of Valgrind is licensed under the
|
|
terms of the GNU General Public License, version 2, unless
|
|
otherwise indicated. See the COPYING file in the source
|
|
distribution for details.
|
|
|
|
----------------------------------------------------------------
|
|
|
|
This file is part of Valgrind, a dynamic binary instrumentation
|
|
framework.
|
|
|
|
Copyright (C) 2006-2015 OpenWorks LLP. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
2. The origin of this software must not be misrepresented; you must
|
|
not claim that you wrote the original software. If you use this
|
|
software in a product, an acknowledgment in the product
|
|
documentation would be appreciated but is not required.
|
|
|
|
3. Altered source versions must be plainly marked as such, and must
|
|
not be misrepresented as being the original software.
|
|
|
|
4. The name of the author may not be used to endorse or promote
|
|
products derived from this software without specific prior written
|
|
permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
|
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
Neither the names of the U.S. Department of Energy nor the
|
|
University of California nor the names of its contributors may be
|
|
used to endorse or promote products derived from this software
|
|
without prior written permission.
|
|
*/
|
|
|
|
/* Handling of MPI_STATUS{ES}_IGNORE for MPI_Status* arguments.
|
|
|
|
The MPI-2 spec allows many functions which have MPI_Status* purely
|
|
as an out parameter, to accept the constants MPI_STATUS_IGNORE or
|
|
MPI_STATUSES_IGNORE there instead, if the caller does not care
|
|
about the status. See the MPI-2 spec sec 4.5.1 ("Passing
|
|
MPI_STATUS_IGNORE for Status"). (mpi2-report.pdf, 1615898 bytes,
|
|
md5=694a5efe2fd291eecf7e8c9875b5f43f).
|
|
|
|
This library handles such cases by allocating a fake MPI_Status
|
|
object (on the stack) or an array thereof (on the heap), and
|
|
passing that onwards instead. From the outside the caller sees no
|
|
difference. Unfortunately the simpler approach of merely detecting
|
|
and handling these special cases at a lower level does not work,
|
|
because we need to use information returned in MPI_Status*
|
|
arguments to paint result buffers, even if the caller doesn't
|
|
supply a real MPI_Status object.
|
|
|
|
Eg, MPI_Recv. We can't paint the result buffer without knowing how
|
|
many items arrived; but we can't find that out without passing a
|
|
real MPI_Status object to the (real) MPI_Recv call. Hence, if the
|
|
caller did not supply one, we have no option but to use a temporary
|
|
stack allocated one for the inner call. Ditto, more indirectly
|
|
(via maybe_complete) for nonblocking receives and the various
|
|
associated wait/test calls. */
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- includes ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <unistd.h> /* getpid */
|
|
#include <stdlib.h> /* exit */
|
|
#include <string.h> /* strstr */
|
|
#include <pthread.h> /* pthread_mutex_{lock,unlock} */
|
|
|
|
/* Include Valgrind magic macros for writing wrappers. */
|
|
#include "../memcheck/memcheck.h"
|
|
|
|
/* Include macros for VALGRIND_{DIS,EN}ABLE_ERROR_REPORTING.
|
|
This is somewhat experimental and hence disable-able, by
|
|
setting cONFIG_DER to zero. */
|
|
#include "../include/valgrind.h"
|
|
|
|
#define cONFIG_DER 1 /* set to 0 to disable */
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Connect to MPI library ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* Include headers for whatever MPI implementation the wrappers are to
|
|
be used with. The configure system will tell us what the path to
|
|
the chosen MPI implementation is, via -I.. to the compiler. */
|
|
#include "mpi.h"
|
|
|
|
/* Where are API symbols?
|
|
Open MPI lib/libmpi.so, soname = libmpi.so.0
|
|
Quadrics MPI lib/libmpi.so, soname = libmpi.so.0
|
|
MPICH libmpich.so.1.0, soname = libmpich.so.1.0
|
|
|
|
A suitable soname to match with is therefore "libmpi*.so*".
|
|
*/
|
|
#define I_WRAP_FNNAME_U(_name) \
|
|
I_WRAP_SONAME_FNNAME_ZU(libmpiZaZdsoZa,_name)
|
|
|
|
|
|
/* Define HAVE_MPI_STATUS_IGNORE iff we have to deal with
|
|
MPI_STATUS{ES}_IGNORE. */
|
|
#if MPI_VERSION >= 2 \
|
|
|| (defined(MPI_STATUS_IGNORE) && defined(MPI_STATUSES_IGNORE))
|
|
# undef HAVE_MPI_STATUS_IGNORE
|
|
# define HAVE_MPI_STATUS_IGNORE 1
|
|
#else
|
|
# undef HAVE_MPI_STATUS_IGNORE
|
|
#endif
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Decls ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
typedef unsigned char Bool;
|
|
#define False ((Bool)0)
|
|
#define True ((Bool)1)
|
|
|
|
/* Word, UWord are machine words - same size as a pointer. This is
|
|
checked at startup. The wrappers below use 'long' to mean a
|
|
machine word - this too is tested at startup. */
|
|
typedef signed long Word;
|
|
typedef unsigned long UWord;
|
|
|
|
#if !defined(offsetof)
|
|
# define offsetof(type,memb) ((UWord)&((type*)0)->memb)
|
|
#endif
|
|
|
|
/* Find the size of long double image (not 'sizeof(long double)').
|
|
See comments in sizeofOneNamedTy. */
|
|
static long sizeof_long_double_image ( void );
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Simple helpers ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* ------ Helpers for debug printing ------ */
|
|
|
|
/* constant */
|
|
static const char* preamble = "valgrind MPI wrappers";
|
|
|
|
/* established at startup */
|
|
static pid_t my_pid = -1;
|
|
static char* options_str = NULL;
|
|
static int opt_verbosity = 1;
|
|
static Bool opt_missing = 0; /* 0:silent; 1:warn; 2:abort */
|
|
static Bool opt_help = False;
|
|
static Bool opt_initkludge = False;
|
|
|
|
static void before ( char* fnname )
|
|
{
|
|
/* This isn't thread-safe wrt 'done' (no locking). It's not
|
|
critical. */
|
|
static int done = 0;
|
|
if (done == 0) {
|
|
done = 1;
|
|
my_pid = getpid();
|
|
options_str = getenv("MPIWRAP_DEBUG");
|
|
if (options_str) {
|
|
if (NULL != strstr(options_str, "warn"))
|
|
opt_missing = 1;
|
|
if (NULL != strstr(options_str, "strict"))
|
|
opt_missing = 2;
|
|
if (NULL != strstr(options_str, "verbose"))
|
|
opt_verbosity++;
|
|
if (NULL != strstr(options_str, "quiet"))
|
|
opt_verbosity--;
|
|
if (NULL != strstr(options_str, "help"))
|
|
opt_help = True;
|
|
if (NULL != strstr(options_str, "initkludge"))
|
|
opt_initkludge = True;
|
|
}
|
|
if (opt_verbosity > 0)
|
|
fprintf(stderr, "%s %5d: Active for pid %d\n",
|
|
preamble, my_pid, my_pid);
|
|
/* Sanity check - that Word/UWord really are machine words. */
|
|
assert(sizeof(Word) == sizeof(void*));
|
|
assert(sizeof(UWord) == sizeof(void*));
|
|
/* Sanity check - char is byte-sized (else address calculations
|
|
in walk_type don't work. */
|
|
assert(sizeof(char) == 1);
|
|
if (opt_help) {
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "Valid options for the MPIWRAP_DEBUG environment"
|
|
" variable are:\n");
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, " quiet be silent except for errors\n");
|
|
fprintf(stderr, " verbose show wrapper entries/exits\n");
|
|
fprintf(stderr, " strict abort the program if a function"
|
|
" with no wrapper is used\n");
|
|
fprintf(stderr, " warn give a warning if a function"
|
|
" with no wrapper is used\n");
|
|
fprintf(stderr, " help display this message, then exit\n");
|
|
fprintf(stderr, " initkludge debugging hack; do not use\n");
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "Multiple options are allowed, eg"
|
|
" MPIWRAP_DEBUG=strict,verbose\n");
|
|
fprintf(stderr, "Note: 'warn' generates output even if 'quiet'"
|
|
" is also specified\n");
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "%s %5d: exiting now\n", preamble, my_pid );
|
|
exit(1);
|
|
}
|
|
if (opt_verbosity > 0)
|
|
fprintf(stderr,
|
|
"%s %5d: Try MPIWRAP_DEBUG=help for possible options\n",
|
|
preamble, my_pid);
|
|
|
|
}
|
|
if (opt_verbosity > 1)
|
|
fprintf(stderr, "%s %5d: enter PMPI_%s\n", preamble, my_pid, fnname );
|
|
}
|
|
|
|
static __inline__ void after ( char* fnname, int err )
|
|
{
|
|
if (opt_verbosity > 1)
|
|
fprintf(stderr, "%s %5d: exit PMPI_%s (err = %d)\n",
|
|
preamble, my_pid, fnname, err );
|
|
}
|
|
|
|
static void barf ( char* msg )
|
|
{
|
|
fprintf(stderr, "%s %5d: fatal: %s\n", preamble, my_pid, msg);
|
|
fprintf(stderr, "%s %5d: exiting now\n", preamble, my_pid );
|
|
exit(1);
|
|
}
|
|
|
|
/* Half-hearted type-showing function (for debugging). */
|
|
static void showTy ( FILE* f, MPI_Datatype ty )
|
|
{
|
|
if (ty == MPI_DATATYPE_NULL) fprintf(f,"DATATYPE_NULL");
|
|
else if (ty == MPI_BYTE) fprintf(f,"BYTE");
|
|
else if (ty == MPI_PACKED) fprintf(f,"PACKED");
|
|
else if (ty == MPI_CHAR) fprintf(f,"CHAR");
|
|
else if (ty == MPI_SHORT) fprintf(f,"SHORT");
|
|
else if (ty == MPI_INT) fprintf(f,"INT");
|
|
else if (ty == MPI_LONG) fprintf(f,"LONG");
|
|
else if (ty == MPI_FLOAT) fprintf(f,"FLOAT");
|
|
else if (ty == MPI_DOUBLE) fprintf(f,"DOUBLE");
|
|
else if (ty == MPI_LONG_DOUBLE) fprintf(f,"LONG_DOUBLE");
|
|
else if (ty == MPI_UNSIGNED_CHAR) fprintf(f,"UNSIGNED_CHAR");
|
|
else if (ty == MPI_UNSIGNED_SHORT) fprintf(f,"UNSIGNED_SHORT");
|
|
else if (ty == MPI_UNSIGNED_LONG) fprintf(f,"UNSIGNED_LONG");
|
|
else if (ty == MPI_UNSIGNED) fprintf(f,"UNSIGNED");
|
|
else if (ty == MPI_FLOAT_INT) fprintf(f,"FLOAT_INT");
|
|
else if (ty == MPI_DOUBLE_INT) fprintf(f,"DOUBLE_INT");
|
|
else if (ty == MPI_LONG_DOUBLE_INT) fprintf(f,"LONG_DOUBLE_INT");
|
|
else if (ty == MPI_LONG_INT) fprintf(f,"LONG_INT");
|
|
else if (ty == MPI_SHORT_INT) fprintf(f,"SHORT_INT");
|
|
else if (ty == MPI_2INT) fprintf(f,"2INT");
|
|
else if (ty == MPI_UB) fprintf(f,"UB");
|
|
else if (ty == MPI_LB) fprintf(f,"LB");
|
|
# if defined(MPI_WCHAR)
|
|
else if (ty == MPI_WCHAR) fprintf(f,"WCHAR");
|
|
# endif
|
|
else if (ty == MPI_LONG_LONG_INT) fprintf(f,"LONG_LONG_INT");
|
|
# if defined(MPI_LONG_LONG)
|
|
else if (ty == MPI_LONG_LONG) fprintf(f,"LONG_LONG");
|
|
# endif
|
|
# if defined(MPI_UNSIGNED_LONG_LONG)
|
|
else if (ty == MPI_UNSIGNED_LONG_LONG) fprintf(f,"UNSIGNED_LONG_LONG");
|
|
# endif
|
|
# if defined(MPI_REAL8)
|
|
else if (ty == MPI_REAL8) fprintf(f, "REAL8");
|
|
# endif
|
|
# if defined(MPI_REAL4)
|
|
else if (ty == MPI_REAL4) fprintf(f, "REAL4");
|
|
# endif
|
|
# if defined(MPI_REAL)
|
|
else if (ty == MPI_REAL) fprintf(f, "REAL");
|
|
# endif
|
|
# if defined(MPI_INTEGER8)
|
|
else if (ty == MPI_INTEGER8) fprintf(f, "INTEGER8");
|
|
# endif
|
|
# if defined(MPI_INTEGER4)
|
|
else if (ty == MPI_INTEGER4) fprintf(f, "INTEGER4");
|
|
# endif
|
|
# if defined(MPI_INTEGER)
|
|
else if (ty == MPI_INTEGER) fprintf(f, "INTEGER");
|
|
# endif
|
|
# if defined(MPI_DOUBLE_PRECISION)
|
|
else if (ty == MPI_DOUBLE_PRECISION) fprintf(f, "DOUBLE_PRECISION");
|
|
# endif
|
|
# if defined(MPI_COMPLEX)
|
|
else if (ty == MPI_COMPLEX) fprintf(f, "COMPLEX");
|
|
# endif
|
|
# if defined(MPI_DOUBLE_COMPLEX)
|
|
else if (ty == MPI_DOUBLE_COMPLEX) fprintf(f, "DOUBLE_COMPLEX");
|
|
# endif
|
|
# if defined(MPI_LOGICAL)
|
|
else if (ty == MPI_LOGICAL) fprintf(f, "LOGICAL");
|
|
# endif
|
|
# if defined(MPI_2INTEGER)
|
|
else if (ty == MPI_2INTEGER) fprintf(f, "2INTEGER");
|
|
# endif
|
|
# if defined(MPI_2COMPLEX)
|
|
else if (ty == MPI_2COMPLEX) fprintf(f, "2COMPLEX");
|
|
# endif
|
|
# if defined(MPI_2DOUBLE_COMPLEX)
|
|
else if (ty == MPI_2DOUBLE_COMPLEX) fprintf(f, "2DOUBLE_COMPLEX");
|
|
# endif
|
|
# if defined(MPI_2REAL)
|
|
else if (ty == MPI_2REAL) fprintf(f, "2REAL");
|
|
# endif
|
|
# if defined(MPI_2DOUBLE_PRECISION)
|
|
else if (ty == MPI_2DOUBLE_PRECISION) fprintf(f, "2DOUBLE_PRECISION");
|
|
# endif
|
|
# if defined(MPI_CHARACTER)
|
|
else if (ty == MPI_CHARACTER) fprintf(f, "CHARACTER");
|
|
# endif
|
|
else fprintf(f,"showTy:???");
|
|
}
|
|
|
|
static void showCombiner ( FILE* f, int combiner )
|
|
{
|
|
switch (combiner) {
|
|
case MPI_COMBINER_NAMED: fprintf(f, "NAMED"); break;
|
|
#if defined(MPI_COMBINER_DUP)
|
|
case MPI_COMBINER_DUP: fprintf(f, "DUP"); break;
|
|
# endif
|
|
case MPI_COMBINER_CONTIGUOUS: fprintf(f, "CONTIGUOUS"); break;
|
|
case MPI_COMBINER_VECTOR: fprintf(f, "VECTOR"); break;
|
|
#if defined(MPI_COMBINER_HVECTOR_INTEGER)
|
|
case MPI_COMBINER_HVECTOR_INTEGER: fprintf(f, "HVECTOR_INTEGER"); break;
|
|
# endif
|
|
case MPI_COMBINER_HVECTOR: fprintf(f, "HVECTOR"); break;
|
|
case MPI_COMBINER_INDEXED: fprintf(f, "INDEXED"); break;
|
|
#if defined(MPI_COMBINER_HINDEXED_INTEGER)
|
|
case MPI_COMBINER_HINDEXED_INTEGER: fprintf(f, "HINDEXED_INTEGER"); break;
|
|
# endif
|
|
case MPI_COMBINER_HINDEXED: fprintf(f, "HINDEXED"); break;
|
|
#if defined(MPI_COMBINER_INDEXED_BLOCK)
|
|
case MPI_COMBINER_INDEXED_BLOCK: fprintf(f, "INDEXED_BLOCK"); break;
|
|
# endif
|
|
#if defined(MPI_COMBINER_STRUCT_INTEGER)
|
|
case MPI_COMBINER_STRUCT_INTEGER: fprintf(f, "STRUCT_INTEGER"); break;
|
|
# endif
|
|
case MPI_COMBINER_STRUCT: fprintf(f, "STRUCT"); break;
|
|
#if defined(MPI_COMBINER_SUBARRAY)
|
|
case MPI_COMBINER_SUBARRAY: fprintf(f, "SUBARRAY"); break;
|
|
# endif
|
|
#if defined(MPI_COMBINER_DARRAY)
|
|
case MPI_COMBINER_DARRAY: fprintf(f, "DARRAY"); break;
|
|
# endif
|
|
#if defined(MPI_COMBINER_F90_REAL)
|
|
case MPI_COMBINER_F90_REAL: fprintf(f, "F90_REAL"); break;
|
|
# endif
|
|
#if defined(MPI_COMBINER_F90_COMPLEX)
|
|
case MPI_COMBINER_F90_COMPLEX: fprintf(f, "F90_COMPLEX"); break;
|
|
# endif
|
|
#if defined(MPI_COMBINER_F90_INTEGER)
|
|
case MPI_COMBINER_F90_INTEGER: fprintf(f, "F90_INTEGER"); break;
|
|
# endif
|
|
#if defined(MPI_COMBINER_RESIZED)
|
|
case MPI_COMBINER_RESIZED: fprintf(f, "RESIZED"); break;
|
|
# endif
|
|
default: fprintf(f, "showCombiner:??"); break;
|
|
}
|
|
}
|
|
|
|
|
|
/* ------ Get useful bits of info ------ */
|
|
|
|
/* Note, PMPI_Comm_rank/size are themselves wrapped. Should work
|
|
fine. */
|
|
|
|
static __inline__ int comm_rank ( MPI_Comm comm )
|
|
{
|
|
int err, r;
|
|
err = PMPI_Comm_rank(comm, &r);
|
|
return err ? 0/*arbitrary*/ : r;
|
|
}
|
|
|
|
static __inline__ int comm_size ( MPI_Comm comm )
|
|
{
|
|
int err, r;
|
|
err = PMPI_Comm_size(comm, &r);
|
|
return err ? 0/*arbitrary*/ : r;
|
|
}
|
|
|
|
static __inline__ Bool count_from_Status( /*OUT*/int* recv_count,
|
|
MPI_Datatype datatype,
|
|
MPI_Status* status)
|
|
{
|
|
int n;
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
int err = PMPI_Get_count(status, datatype, &n);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
if (err == MPI_SUCCESS) {
|
|
VALGRIND_MAKE_MEM_DEFINED(&n, sizeof(n));
|
|
*recv_count = n;
|
|
return True;
|
|
} else {
|
|
return False;
|
|
}
|
|
}
|
|
|
|
/* It's critical that we can do equality on MPI_Requests.
|
|
Unfortunately these are opaque objects to us (handles, in the
|
|
parlance of the MPI 1.1 spec). Fortunately Sec 2.4.1 ("Opaque
|
|
Objects") specifies that "In C, [...] These [handles] should be
|
|
types that support assignment and equality operations." Hence the
|
|
following function should compile for any compliant definition of
|
|
MPI_Request. */
|
|
static __inline__
|
|
Bool eq_MPI_Request ( MPI_Request r1, MPI_Request r2 )
|
|
{
|
|
return r1 == r2;
|
|
}
|
|
|
|
/* Return True if status is MPI_STATUS_IGNORE or MPI_STATUSES_IGNORE.
|
|
On MPI-1.x platforms which don't have these symbols (and they would
|
|
only have them if they've been backported from 2.x) always return
|
|
False. */
|
|
static __inline__
|
|
Bool isMSI ( MPI_Status* status )
|
|
{
|
|
# if defined(HAVE_MPI_STATUS_IGNORE)
|
|
return status == MPI_STATUSES_IGNORE || status == MPI_STATUS_IGNORE;
|
|
# else
|
|
return False;
|
|
# endif
|
|
}
|
|
|
|
/* Get the 'extent' of a type. Note, as per the MPI spec this
|
|
includes whatever padding would be required when using 'ty' in an
|
|
array. */
|
|
static long extentOfTy ( MPI_Datatype ty )
|
|
{
|
|
int r;
|
|
MPI_Aint n;
|
|
r = PMPI_Type_extent(ty, &n);
|
|
assert(r == MPI_SUCCESS);
|
|
return (long)n;
|
|
}
|
|
|
|
/* Free up *ty, if it is safe to do so */
|
|
static void maybeFreeTy ( MPI_Datatype* ty )
|
|
{
|
|
int r, n_ints, n_addrs, n_dtys, tycon;
|
|
|
|
r = PMPI_Type_get_envelope( *ty, &n_ints, &n_addrs, &n_dtys, &tycon );
|
|
assert(r == MPI_SUCCESS);
|
|
|
|
/* can't free named types */
|
|
if (tycon == MPI_COMBINER_NAMED)
|
|
return;
|
|
|
|
/* some kinds of structs are predefined so we can't free them
|
|
either. */
|
|
if (*ty == MPI_FLOAT_INT || *ty == MPI_DOUBLE_INT
|
|
|| *ty == MPI_LONG_INT || *ty == MPI_2INT
|
|
|| *ty == MPI_SHORT_INT || *ty == MPI_LONG_DOUBLE_INT)
|
|
return;
|
|
|
|
/* Looks OK - free it. */
|
|
if (0) {
|
|
/* show me what you're about to free .. */
|
|
fprintf(stderr, "freeing combiner ");
|
|
showCombiner(stderr,tycon);
|
|
fprintf(stderr, " ty= ");
|
|
showTy(stderr,*ty);
|
|
fprintf(stderr,"\n");
|
|
}
|
|
r = PMPI_Type_free(ty);
|
|
assert(r == MPI_SUCCESS);
|
|
}
|
|
|
|
/* How big is a "named" (base) type? Returns 0 if not known. Note.
|
|
There is a subtlety, which is that this is required to return the
|
|
exact size of one item of the type, NOT the size of it when padded
|
|
suitably to make an array of them. In particular that's why the
|
|
size of LONG_DOUBLE is computed by looking at the result of doing a
|
|
long double store, rather than just asking what is the sizeof(long
|
|
double).
|
|
|
|
For LONG_DOUBLE on x86-linux and amd64-linux my impression is that
|
|
the right answer is 10 even though sizeof(long double) says 12 and
|
|
16 respectively. On ppc32-linux it appears to be 16.
|
|
|
|
Ref: MPI 1.1 doc p18 */
|
|
static long sizeofOneNamedTy ( MPI_Datatype ty )
|
|
{
|
|
if (ty == MPI_CHAR) return sizeof(signed char);
|
|
if (ty == MPI_SHORT) return sizeof(signed short int);
|
|
if (ty == MPI_INT) return sizeof(signed int);
|
|
if (ty == MPI_LONG) return sizeof(signed long int);
|
|
if (ty == MPI_UNSIGNED_CHAR) return sizeof(unsigned char);
|
|
if (ty == MPI_UNSIGNED_SHORT) return sizeof(unsigned short int);
|
|
if (ty == MPI_UNSIGNED) return sizeof(unsigned int);
|
|
if (ty == MPI_UNSIGNED_LONG) return sizeof(unsigned long int);
|
|
if (ty == MPI_FLOAT) return sizeof(float);
|
|
if (ty == MPI_DOUBLE) return sizeof(double);
|
|
if (ty == MPI_BYTE) return 1;
|
|
if (ty == MPI_LONG_DOUBLE) return sizeof_long_double_image();
|
|
if (ty == MPI_PACKED) return 1;
|
|
if (ty == MPI_LONG_LONG_INT) return sizeof(signed long long int);
|
|
|
|
# if defined(MPI_REAL8)
|
|
if (ty == MPI_REAL8) return 8; /* MPI2 spec */;
|
|
# endif
|
|
# if defined(MPI_REAL4)
|
|
if (ty == MPI_REAL4) return 4; /* MPI2 spec */;
|
|
# endif
|
|
# if defined(MPI_REAL)
|
|
if (ty == MPI_REAL) return 4; /* MPI2 spec */;
|
|
# endif
|
|
# if defined(MPI_INTEGER8)
|
|
if (ty == MPI_INTEGER8) return 8; /* MPI2 spec */;
|
|
# endif
|
|
# if defined(MPI_INTEGER4)
|
|
if (ty == MPI_INTEGER4) return 4; /* MPI2 spec */;
|
|
# endif
|
|
# if defined(MPI_INTEGER)
|
|
if (ty == MPI_INTEGER) return 4; /* MPI2 spec */;
|
|
# endif
|
|
# if defined(MPI_DOUBLE_PRECISION)
|
|
if (ty == MPI_DOUBLE_PRECISION) return 8; /* MPI2 spec */;
|
|
# endif
|
|
|
|
/* new in MPI2: */
|
|
# if defined(MPI_WCHAR)
|
|
if (ty == MPI_WCHAR) return 2; /* MPI2 spec */;
|
|
# endif
|
|
# if defined(MPI_SIGNED_CHAR)
|
|
if (ty == MPI_SIGNED_CHAR) return 1; /* MPI2 spec */;
|
|
# endif
|
|
# if defined(MPI_UNSIGNED_LONG_LONG)
|
|
if (ty == MPI_UNSIGNED_LONG_LONG) return 8; /* MPI2 spec */;
|
|
# endif
|
|
# if defined(MPI_COMPLEX)
|
|
if (ty == MPI_COMPLEX) return 2 * 4; /* MPI2 spec */
|
|
# endif
|
|
# if defined(MPI_DOUBLE_COMPLEX)
|
|
if (ty == MPI_DOUBLE_COMPLEX) return 2 * 8; /* MPI2 spec */
|
|
# endif
|
|
# if defined(MPI_LOGICAL)
|
|
if (ty == MPI_LOGICAL) return 4; /* MPI2 spec */
|
|
# endif
|
|
# if defined(MPI_2INTEGER)
|
|
if (ty == MPI_2INTEGER) return 2 * 4; /* undocumented in MPI2 */
|
|
# endif
|
|
# if defined(MPI_2COMPLEX)
|
|
if (ty == MPI_2COMPLEX) return 2 * 8; /* undocumented in MPI2 */
|
|
# endif
|
|
# if defined(MPI_2DOUBLE_COMPLEX)
|
|
/* 32: this is how openmpi-1.2.2 behaves on x86-linux, but I have
|
|
really no idea if this is right. */
|
|
if (ty == MPI_2DOUBLE_COMPLEX) return 32; /* undocumented in MPI2 */
|
|
# endif
|
|
# if defined(MPI_2REAL)
|
|
if (ty == MPI_2REAL) return 2 * 4; /* undocumented in MPI2 */
|
|
# endif
|
|
# if defined(MPI_2DOUBLE_PRECISION)
|
|
if (ty == MPI_2DOUBLE_PRECISION) return 2 * 8; /* undocumented in MPI2 */
|
|
# endif
|
|
# if defined(MPI_CHARACTER)
|
|
if (ty == MPI_CHARACTER) return 1; /* MPI2 spec */
|
|
# endif
|
|
|
|
/* Note: the following are named structs, not named basic types,
|
|
and so are not handled here:
|
|
FLOAT_INT DOUBLE_INT LONG_INT 2INT SHORT_INT LONG_DOUBLE_INT
|
|
My guess is they are probably for doing max-w-index style
|
|
reductions, the INT carrying the index of the max/min and the
|
|
other type its actual value.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Find the size of long double image (not 'sizeof(long double)').
|
|
See comments in sizeofOneNamedTy.
|
|
*/
|
|
static long sizeof_long_double_image ( void )
|
|
{
|
|
long i;
|
|
unsigned char* p;
|
|
static long cached_result = 0;
|
|
|
|
/* Hopefully we have it already. */
|
|
if (cached_result != 0) {
|
|
assert(cached_result == 10 || cached_result == 16 || cached_result == 8);
|
|
return cached_result;
|
|
}
|
|
|
|
/* No? Then we'll have to compute it. This isn't thread-safe but
|
|
it doesn't really matter since all races to compute it should
|
|
produce the same answer. */
|
|
p = malloc(64);
|
|
assert(p);
|
|
for (i = 0; i < 64; i++)
|
|
p[i] = 0x55;
|
|
|
|
/* Write a value which isn't known at compile time and therefore
|
|
must come out of a register. If we just store a constant here,
|
|
some compilers write more data than a store from a machine
|
|
register would. Therefore we have to force a store from a
|
|
machine register by storing a value which isn't known at compile
|
|
time. Since getpid() will return a value < 1 million, turn it
|
|
into a zero by dividing by 1e+30. */
|
|
*(long double*)(&p[16]) = (long double)(1.0e-30 * (double)getpid());
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
assert(p[i] == 0x55);
|
|
assert(p[i+48] == 0x55);
|
|
}
|
|
for (i = 16; i <= 48; i++) {
|
|
if (p[i] == 0x55)
|
|
break;
|
|
}
|
|
|
|
assert(i < 48);
|
|
assert(i > 16);
|
|
free(p);
|
|
cached_result = i - 16;
|
|
|
|
if (0)
|
|
printf("sizeof_long_double_image: computed %d\n", (int)cached_result);
|
|
|
|
assert(cached_result == 10 || cached_result == 16 || cached_result == 8);
|
|
return cached_result;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Unpicking datatypes ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
static __inline__
|
|
void walk_type_array ( void(*f)(void*,long), char* base,
|
|
MPI_Datatype ty, long count );
|
|
|
|
|
|
/* Walk over all fragments of the object of type 'ty' with base
|
|
address 'base', and apply 'f' to the start/length of each
|
|
contiguous fragment. */
|
|
static
|
|
void walk_type ( void(*f)(void*,long), char* base, MPI_Datatype ty )
|
|
{
|
|
int r, n_ints, n_addrs, n_dtys, tycon;
|
|
long ex, i;
|
|
int* ints = NULL;
|
|
MPI_Aint* addrs = NULL;
|
|
MPI_Datatype* dtys = NULL;
|
|
|
|
/* Stuff for limiting how much complaining text it spews out */
|
|
static int complaints = 3;
|
|
static int last_complained_about_tycon = -987654321; /* presumably bogus */
|
|
|
|
if (0)
|
|
printf("walk_type %p\n", (void*)(unsigned long)ty);
|
|
|
|
r = PMPI_Type_get_envelope( ty, &n_ints, &n_addrs, &n_dtys, &tycon );
|
|
assert(r == MPI_SUCCESS);
|
|
|
|
/* Handle the base cases fast(er/ish). */
|
|
if (tycon == MPI_COMBINER_NAMED) {
|
|
long sz = sizeofOneNamedTy(ty);
|
|
if (sz > 0) {
|
|
f(base, sz);
|
|
return;
|
|
}
|
|
/* Hmm. Perhaps it's a named struct? Unfortunately we can't
|
|
take them to bits so we have to do a really ugly hack, which
|
|
makes assumptions about how the MPI implementation has laid
|
|
out these types. At least Open MPI 1.0.1 appears to put
|
|
the 'val' field first. MPICH2 agrees.
|
|
*/
|
|
if (ty == MPI_2INT) {
|
|
typedef struct { int val; int loc; } Ty;
|
|
f(base + offsetof(Ty,val), sizeof(int));
|
|
f(base + offsetof(Ty,loc), sizeof(int));
|
|
return;
|
|
}
|
|
if (ty == MPI_LONG_INT) {
|
|
typedef struct { long val; int loc; } Ty;
|
|
f(base + offsetof(Ty,val), sizeof(long));
|
|
f(base + offsetof(Ty,loc), sizeof(int));
|
|
return;
|
|
}
|
|
if (ty == MPI_DOUBLE_INT) {
|
|
typedef struct { double val; int loc; } Ty;
|
|
f(base + offsetof(Ty,val), sizeof(double));
|
|
f(base + offsetof(Ty,loc), sizeof(int));
|
|
return;
|
|
}
|
|
if (ty == MPI_SHORT_INT) {
|
|
typedef struct { short val; int loc; } Ty;
|
|
f(base + offsetof(Ty,val), sizeof(short));
|
|
f(base + offsetof(Ty,loc), sizeof(int));
|
|
return;
|
|
}
|
|
if (ty == MPI_FLOAT_INT) {
|
|
typedef struct { float val; int loc; } Ty;
|
|
f(base + offsetof(Ty,val), sizeof(float));
|
|
f(base + offsetof(Ty,loc), sizeof(int));
|
|
return;
|
|
}
|
|
if (ty == MPI_LONG_DOUBLE_INT) {
|
|
typedef struct { long double val; int loc; } Ty;
|
|
f(base + offsetof(Ty,val), sizeof_long_double_image());
|
|
f(base + offsetof(Ty,loc), sizeof(int));
|
|
return;
|
|
}
|
|
if (ty == MPI_LB || ty == MPI_UB)
|
|
return; /* have zero size, so nothing needs to be done */
|
|
goto unhandled;
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
if (0) {
|
|
ex = extentOfTy(ty);
|
|
printf("tycon 0x%llx %d %d %d (ext %d)\n",
|
|
(unsigned long long int)tycon,
|
|
n_ints, n_addrs, n_dtys, (int)ex );
|
|
}
|
|
|
|
/* Now safe to do MPI_Type_get_contents */
|
|
assert(n_ints >= 0);
|
|
assert(n_addrs >= 0);
|
|
assert(n_dtys >= 0);
|
|
|
|
if (n_ints > 0) {
|
|
ints = malloc(n_ints * sizeof(int));
|
|
assert(ints);
|
|
}
|
|
if (n_addrs > 0) {
|
|
addrs = malloc(n_addrs * sizeof(MPI_Aint));
|
|
assert(addrs);
|
|
}
|
|
if (n_dtys > 0) {
|
|
dtys = malloc(n_dtys * sizeof(MPI_Datatype));
|
|
assert(dtys);
|
|
}
|
|
|
|
r = PMPI_Type_get_contents( ty, n_ints, n_addrs, n_dtys,
|
|
ints, addrs, dtys );
|
|
assert(r == MPI_SUCCESS);
|
|
|
|
switch (tycon) {
|
|
|
|
case MPI_COMBINER_CONTIGUOUS:
|
|
assert(n_ints == 1 && n_addrs == 0 && n_dtys == 1);
|
|
walk_type_array( f, base, dtys[0], ints[0] );
|
|
maybeFreeTy( &dtys[0] );
|
|
break;
|
|
|
|
case MPI_COMBINER_VECTOR:
|
|
assert(n_ints == 3 && n_addrs == 0 && n_dtys == 1);
|
|
ex = extentOfTy(dtys[0]);
|
|
if (0)
|
|
printf("vector count %d x (bl %d stride %d)\n",
|
|
(int)ints[0], (int)ints[1], (int)ints[2]);
|
|
for (i = 0; i < ints[0]; i++) {
|
|
walk_type_array( f, base + i * ints[2]/*stride*/ * ex,
|
|
dtys[0], ints[1]/*blocklength*/ );
|
|
}
|
|
maybeFreeTy( &dtys[0] );
|
|
break;
|
|
|
|
case MPI_COMBINER_HVECTOR:
|
|
assert(n_ints == 2 && n_addrs == 1 && n_dtys == 1);
|
|
ex = extentOfTy(dtys[0]);
|
|
if (0)
|
|
printf("hvector count %d x (bl %d hstride %d)\n",
|
|
(int)ints[0], (int)ints[1], (int)addrs[0]);
|
|
for (i = 0; i < ints[0]; i++) {
|
|
walk_type_array( f, base + i * addrs[0]/*hstride*/,
|
|
dtys[0], ints[1]/*blocklength*/ );
|
|
}
|
|
maybeFreeTy( &dtys[0] );
|
|
break;
|
|
|
|
case MPI_COMBINER_INDEXED:
|
|
assert(n_addrs == 0 && n_dtys == 1);
|
|
assert(n_ints > 0);
|
|
assert(n_ints == 2 * ints[0] + 1);
|
|
ex = extentOfTy(dtys[0]);
|
|
for (i = 0; i < ints[0]; i++) {
|
|
if (0)
|
|
printf("indexed (elem %d) off %d copies %d\n",
|
|
(int)i, ints[i+1+ints[0]], ints[i+1] );
|
|
walk_type_array( f, base + ex * ints[i+1+ints[0]],
|
|
dtys[0], ints[i+1] );
|
|
}
|
|
maybeFreeTy( &dtys[0] );
|
|
break;
|
|
|
|
case MPI_COMBINER_HINDEXED:
|
|
assert(n_ints > 0);
|
|
assert(n_ints == ints[0] + 1);
|
|
assert(n_addrs == ints[0] && n_dtys == 1);
|
|
ex = extentOfTy(dtys[0]);
|
|
for (i = 0; i < ints[0]; i++) {
|
|
if (0)
|
|
printf("hindexed (elem %d) hoff %d copies %d\n",
|
|
(int)i, (int)addrs[i], ints[i+1] );
|
|
walk_type_array( f, base + addrs[i],
|
|
dtys[0], ints[i+1] );
|
|
}
|
|
maybeFreeTy( &dtys[0] );
|
|
break;
|
|
|
|
case MPI_COMBINER_STRUCT:
|
|
assert(n_addrs == n_ints-1);
|
|
assert(n_dtys == n_ints-1);
|
|
assert(n_ints > 0);
|
|
assert(n_ints == ints[0] + 1);
|
|
for (i = 0; i < ints[0]; i++) {
|
|
if (0)
|
|
printf("struct (elem %d limit %d) hoff %d copies %d\n",
|
|
(int)i, (int)ints[0], (int)addrs[i], (int)ints[i+1]);
|
|
walk_type_array( f, base + addrs[i], dtys[i], (long)ints[i+1] );
|
|
maybeFreeTy( &dtys[i] );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
goto unhandled;
|
|
|
|
}
|
|
|
|
/* normal exit */
|
|
if (ints) free(ints);
|
|
if (addrs) free(addrs);
|
|
if (dtys) free(dtys);
|
|
return;
|
|
|
|
unhandled:
|
|
/* Complain, but limit the amount of complaining that can happen to
|
|
the first 3 different unhandled tycons that show up, so as to
|
|
avoid swamping users with thousands of duplicate messages. */
|
|
if (complaints > 0 && tycon != last_complained_about_tycon) {
|
|
complaints--;
|
|
last_complained_about_tycon = tycon;
|
|
if (tycon == MPI_COMBINER_NAMED) {
|
|
fprintf(stderr, "%s %5d: walk_type: unhandled base type 0x%lx ",
|
|
preamble, my_pid, (long)ty);
|
|
showTy(stderr, ty);
|
|
fprintf(stderr, "\n");
|
|
} else {
|
|
fprintf(stderr, "%s %5d: walk_type: unhandled combiner 0x%lx\n",
|
|
preamble, my_pid, (long)tycon);
|
|
}
|
|
}
|
|
if (ints) free(ints);
|
|
if (addrs) free(addrs);
|
|
if (dtys) free(dtys);
|
|
if (opt_missing >= 2)
|
|
barf("walk_type: unhandled combiner, strict checking selected");
|
|
}
|
|
|
|
|
|
/* Same as walk_type but apply 'f' to every element in an array of
|
|
'count' items starting at 'base'. The only purpose of pushing this
|
|
into a different routine is so it can attempt to optimise the case
|
|
where the array elements are contiguous and packed together without
|
|
holes. */
|
|
static __inline__
|
|
void walk_type_array ( void(*f)(void*,long), char* base,
|
|
MPI_Datatype elemTy, long count )
|
|
{
|
|
long i, ex;
|
|
|
|
assert(sizeof(unsigned long) == sizeof(char*));
|
|
|
|
/* First see if we can do this the fast way. */
|
|
ex = sizeofOneNamedTy(elemTy);
|
|
|
|
if ( /* ty is a primitive type with power-of-2 size */
|
|
(ex == 8 || ex == 4 || ex == 2 || ex == 1)
|
|
&& /* base is suitably aligned for ty */
|
|
( ((unsigned long)base) & (ex-1)) == 0) {
|
|
|
|
/* We're sure it's contiguous, so just paint/check it in one
|
|
go. */
|
|
if (0) printf("walk_type_array fast %ld of size %ld\n", count, ex );
|
|
f ( base, count * ex );
|
|
|
|
} else {
|
|
|
|
/* Bad news. We have to futz with each element individually.
|
|
This could be very expensive.
|
|
|
|
Note: subtle. If ty is LONG_DOUBLE then the extent will be
|
|
12, so the following loop will jump along in steps of 12, but
|
|
the size painted by walk_type will be 10 since it uses
|
|
sizeofOneNamedTy to establish the size of base types. Which
|
|
is what we need to happen. */
|
|
ex = extentOfTy(elemTy);
|
|
if (0) printf("walk_type_array SLOW %ld of size %ld\n", count, ex );
|
|
for (i = 0; i < count; i++)
|
|
walk_type( f, base + i * ex, elemTy );
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* Hook so it's visible from outside (can be handy to dlopen/dlsym
|
|
it) */
|
|
void mpiwrap_walk_type_EXTERNALLY_VISIBLE
|
|
( void(*f)(void*,long), char* base, MPI_Datatype ty )
|
|
{
|
|
walk_type(f, base, ty);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Address-range helpers ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* ----------------
|
|
Do corresponding checks on memory areas defined using a
|
|
straightforward (start, length) description.
|
|
----------------
|
|
*/
|
|
|
|
static __inline__
|
|
void check_mem_is_defined_untyped ( void* buffer, long nbytes )
|
|
{
|
|
if (nbytes > 0) {
|
|
VALGRIND_CHECK_MEM_IS_DEFINED(buffer, nbytes);
|
|
}
|
|
}
|
|
|
|
static __inline__
|
|
void check_mem_is_addressable_untyped ( void* buffer, long nbytes )
|
|
{
|
|
if (nbytes > 0) {
|
|
VALGRIND_CHECK_MEM_IS_ADDRESSABLE(buffer, nbytes);
|
|
}
|
|
}
|
|
|
|
static __inline__
|
|
void make_mem_defined_if_addressable_untyped ( void* buffer, long nbytes )
|
|
{
|
|
if (nbytes > 0) {
|
|
VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(buffer, nbytes);
|
|
}
|
|
}
|
|
|
|
static __inline__
|
|
void make_mem_defined_if_addressable_if_success_untyped ( int err,
|
|
void* buffer, long nbytes )
|
|
{
|
|
if (err == MPI_SUCCESS && nbytes > 0) {
|
|
VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(buffer, nbytes);
|
|
}
|
|
}
|
|
|
|
|
|
/* ----------------
|
|
Do checks on memory areas defined using the MPI (buffer, count,
|
|
type) convention.
|
|
----------------
|
|
*/
|
|
|
|
/* Check that the specified area is both addressible and contains
|
|
initialised data, and cause V to complain if not. */
|
|
|
|
static __inline__
|
|
void check_mem_is_defined ( char* buffer, long count, MPI_Datatype datatype )
|
|
{
|
|
walk_type_array( check_mem_is_defined_untyped, buffer, datatype, count );
|
|
}
|
|
|
|
|
|
/* Check that the specified area is addressible, and cause V to
|
|
complain if not. Doesn't matter whether the data there is
|
|
initialised or not. */
|
|
|
|
static __inline__
|
|
void check_mem_is_addressable ( void *buffer, long count, MPI_Datatype datatype )
|
|
{
|
|
walk_type_array( check_mem_is_addressable_untyped, buffer, datatype, count );
|
|
}
|
|
|
|
|
|
/* Set the specified area to 'defined for each byte which is
|
|
addressible' state. */
|
|
|
|
static __inline__
|
|
void make_mem_defined_if_addressable ( void *buffer, int count, MPI_Datatype datatype )
|
|
{
|
|
walk_type_array( make_mem_defined_if_addressable_untyped,
|
|
buffer, datatype, count );
|
|
}
|
|
|
|
static __inline__
|
|
void
|
|
make_mem_defined_if_addressable_if_success ( int err, void *buffer, int count,
|
|
MPI_Datatype datatype )
|
|
{
|
|
if (err == MPI_SUCCESS)
|
|
make_mem_defined_if_addressable(buffer, count, datatype);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- The wrappers proper. They are listed in the order ---*/
|
|
/*--- in which they appear in "MPI: A Message-Passing ---*/
|
|
/*--- Interface Standard, MPIF, Nov 15 2003" (the MPI 1.1 ---*/
|
|
/*--- spec. All unimplemented wrappers are listed at the ---*/
|
|
/*--- end of the file. The list of function names is ---*/
|
|
/*--- taken from the headers of Open MPI svn r9191. ---*/
|
|
/*--- Hopefully it is a complete list of all the MPI 2 ---*/
|
|
/*--- functions. ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* Handy abbreviation */
|
|
#define WRAPPER_FOR(name) I_WRAP_FNNAME_U(name)
|
|
|
|
/* Generates (conceptually) a wrapper which does nothing. In
|
|
fact just generate no wrapper at all. */
|
|
#define HAS_NO_WRAPPER(basename) /* */
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Sec 3.2, Blocking Send and Receive Operations ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* --- {,B,S,R}Send --- */
|
|
/* pre: rd: (buf,count,datatype) */
|
|
static
|
|
int generic_Send(void *buf, int count, MPI_Datatype datatype,
|
|
int dest, int tag, MPI_Comm comm)
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("{,B,S,R}Send");
|
|
check_mem_is_defined(buf, count, datatype);
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_6W(err, fn, buf,count,datatype,dest,tag,comm);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
after("{,B,S,R}Send", err);
|
|
return err;
|
|
}
|
|
int WRAPPER_FOR(PMPI_Send)(void *buf, int count, MPI_Datatype datatype,
|
|
int dest, int tag, MPI_Comm comm) {
|
|
return generic_Send(buf,count,datatype, dest,tag,comm);
|
|
}
|
|
int WRAPPER_FOR(PMPI_Bsend)(void *buf, int count, MPI_Datatype datatype,
|
|
int dest, int tag, MPI_Comm comm) {
|
|
return generic_Send(buf,count,datatype, dest,tag,comm);
|
|
}
|
|
int WRAPPER_FOR(PMPI_Ssend)(void *buf, int count, MPI_Datatype datatype,
|
|
int dest, int tag, MPI_Comm comm) {
|
|
return generic_Send(buf,count,datatype, dest,tag,comm);
|
|
}
|
|
int WRAPPER_FOR(PMPI_Rsend)(void *buf, int count, MPI_Datatype datatype,
|
|
int dest, int tag, MPI_Comm comm) {
|
|
return generic_Send(buf,count,datatype, dest,tag,comm);
|
|
}
|
|
|
|
/* --- Recv --- */
|
|
/* pre: must be writable: (buf,count,datatype)
|
|
must be writable: status
|
|
post: make readable: (buf,recv_count,datatype)
|
|
where recv_count is determined from *status
|
|
*/
|
|
int WRAPPER_FOR(PMPI_Recv)(void *buf, int count, MPI_Datatype datatype,
|
|
int source, int tag,
|
|
MPI_Comm comm, MPI_Status *status)
|
|
{
|
|
OrigFn fn;
|
|
int err, recv_count = 0;
|
|
MPI_Status fake_status;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Recv");
|
|
if (isMSI(status))
|
|
status = &fake_status;
|
|
check_mem_is_addressable(buf, count, datatype);
|
|
check_mem_is_addressable_untyped(status, sizeof(*status));
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_7W(err, fn, buf,count,datatype,source,tag,comm,status);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
make_mem_defined_if_addressable_if_success_untyped(err, status, sizeof(*status));
|
|
if (err == MPI_SUCCESS && count_from_Status(&recv_count,datatype,status)) {
|
|
make_mem_defined_if_addressable(buf, recv_count, datatype);
|
|
}
|
|
after("Recv", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Get_count --- */
|
|
/* pre: must be readable: *status
|
|
post: make defined: *count -- don't bother, libmpi will surely do this
|
|
*/
|
|
int WRAPPER_FOR(PMPI_Get_count)(MPI_Status* status,
|
|
MPI_Datatype ty, int* count )
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Get_count");
|
|
check_mem_is_defined_untyped(status, sizeof(*status));
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_WWW(err, fn, status,ty,count);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
after("Get_count", err);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Sec 3.7, Nonblocking communication ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* Maintain a table that makes it possible for the wrappers to
|
|
complete MPI_Irecv successfully.
|
|
|
|
The issue is that MPI_Irecv states the recv buffer and returns
|
|
immediately, giving a handle (MPI_Request) for the transaction.
|
|
Later the user will have to poll for completion with MPI_Wait etc,
|
|
and at that point these wrappers have to paint the recv buffer.
|
|
But the recv buffer details are not presented to MPI_Wait - only
|
|
the handle is. We therefore have to use a shadow table
|
|
(sReqs{,_size,_used,_lock}) which associates uncompleted
|
|
MPI_Requests with the corresponding buffer address/count/type.
|
|
|
|
Only read requests are placed in the table, since there is no need
|
|
to do any buffer painting following completion of an Isend - all
|
|
the checks for that are done at the time Isend is called.
|
|
|
|
Care has to be take to remove completed requests from the table.
|
|
|
|
Access to the table is guarded by sReqs_lock so as to make it
|
|
thread-safe.
|
|
*/
|
|
|
|
typedef
|
|
struct {
|
|
Bool inUse;
|
|
MPI_Request key;
|
|
void* buf;
|
|
int count;
|
|
MPI_Datatype datatype;
|
|
}
|
|
ShadowRequest;
|
|
|
|
static ShadowRequest* sReqs = NULL;
|
|
static int sReqs_size = 0;
|
|
static int sReqs_used = 0;
|
|
static pthread_mutex_t sReqs_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
#define LOCK_SREQS \
|
|
do { int pr = pthread_mutex_lock(&sReqs_lock); \
|
|
assert(pr == 0); \
|
|
} while (0)
|
|
|
|
#define UNLOCK_SREQS \
|
|
do { int pr = pthread_mutex_unlock(&sReqs_lock); \
|
|
assert(pr == 0); \
|
|
} while (0)
|
|
|
|
|
|
/* Ensure the sReqs expandable array has at least one free slot, by
|
|
copying it into a larger one if necessary. NOTE: sReqs_lock is
|
|
held throughout this procedure.*/
|
|
static void ensure_sReq_space ( void )
|
|
{
|
|
int i;
|
|
ShadowRequest* sReqs2;
|
|
if (sReqs_used == sReqs_size) {
|
|
sReqs_size = sReqs_size==0 ? 2 : 2*sReqs_size;
|
|
sReqs2 = malloc( sReqs_size * sizeof(ShadowRequest) );
|
|
if (sReqs2 == NULL) {
|
|
UNLOCK_SREQS;
|
|
barf("add_shadow_Request: malloc failed.\n");
|
|
}
|
|
for (i = 0; i < sReqs_used; i++)
|
|
sReqs2[i] = sReqs[i];
|
|
if (sReqs)
|
|
free(sReqs);
|
|
sReqs = sReqs2;
|
|
}
|
|
assert(sReqs_used < sReqs_size);
|
|
}
|
|
|
|
|
|
/* Find shadow info for 'request', or NULL if none. */
|
|
|
|
static
|
|
ShadowRequest* find_shadow_Request ( MPI_Request request )
|
|
{
|
|
ShadowRequest* ret = NULL;
|
|
int i;
|
|
LOCK_SREQS;
|
|
for (i = 0; i < sReqs_used; i++) {
|
|
if (sReqs[i].inUse && eq_MPI_Request(sReqs[i].key,request)) {
|
|
ret = &sReqs[i];
|
|
break;
|
|
}
|
|
}
|
|
UNLOCK_SREQS;
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Delete shadow info for 'request', if any. */
|
|
|
|
static void delete_shadow_Request ( MPI_Request request )
|
|
{
|
|
int i;
|
|
LOCK_SREQS;
|
|
for (i = 0; i < sReqs_used; i++) {
|
|
if (sReqs[i].inUse && eq_MPI_Request(sReqs[i].key,request)) {
|
|
sReqs[i].inUse = False;
|
|
break;
|
|
}
|
|
}
|
|
UNLOCK_SREQS;
|
|
}
|
|
|
|
|
|
/* Add a shadow for 'request', overwriting any old binding for it. */
|
|
|
|
static
|
|
void add_shadow_Request( MPI_Request request,
|
|
void* buf, int count,
|
|
MPI_Datatype datatype )
|
|
{
|
|
int i, ix = -1;
|
|
LOCK_SREQS;
|
|
assert(sReqs_used >= 0);
|
|
assert(sReqs_size >= 0);
|
|
assert(sReqs_used <= sReqs_size);
|
|
if (sReqs == NULL) assert(sReqs_size == 0);
|
|
|
|
/* First of all see if we already have a binding for this key; if
|
|
so just replace it, and have done. */
|
|
for (i = 0; i < sReqs_used; i++) {
|
|
if (sReqs[i].inUse && eq_MPI_Request(sReqs[i].key,request)) {
|
|
ix = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ix < 0) {
|
|
/* Ok, we don't have it, so will have to add it. First search
|
|
to see if there is an existing empty slot. */
|
|
for (i = 0; i < sReqs_used; i++) {
|
|
if (!sReqs[i].inUse) {
|
|
ix = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* No empty slots. Allocate a new one. */
|
|
if (ix < 0) {
|
|
ensure_sReq_space();
|
|
assert(sReqs_used < sReqs_size);
|
|
ix = sReqs_used;
|
|
sReqs_used++;
|
|
}
|
|
|
|
assert(ix >= 0 && ix < sReqs_used);
|
|
assert(sReqs_used <= sReqs_size);
|
|
|
|
sReqs[ix].inUse = True;
|
|
sReqs[ix].key = request;
|
|
sReqs[ix].buf = buf;
|
|
sReqs[ix].count = count;
|
|
sReqs[ix].datatype = datatype;
|
|
|
|
UNLOCK_SREQS;
|
|
if (opt_verbosity > 1)
|
|
fprintf(stderr, "%s %5d: sReq+ 0x%lx -> b/c/d %p/%d/0x%lx [slot %d]\n",
|
|
preamble, my_pid, (unsigned long)request,
|
|
buf, count, (long)datatype, ix);
|
|
}
|
|
|
|
static
|
|
MPI_Request* clone_Request_array ( int count, MPI_Request* orig )
|
|
{
|
|
MPI_Request* copy;
|
|
int i;
|
|
LOCK_SREQS;
|
|
if (count < 0)
|
|
count = 0; /* Hmm. Call Mulder and Scully. */
|
|
copy = malloc( count * sizeof(MPI_Request) );
|
|
if (copy == NULL && count > 0) {
|
|
UNLOCK_SREQS;
|
|
barf("clone_Request_array: malloc failed");
|
|
}
|
|
for (i = 0; i < count; i++)
|
|
copy[i] = orig[i];
|
|
UNLOCK_SREQS;
|
|
return copy;
|
|
}
|
|
|
|
#undef LOCK_SREQS
|
|
#undef UNLOCK_SREQS
|
|
|
|
|
|
static void maybe_complete ( Bool error_in_status,
|
|
MPI_Request request_before,
|
|
MPI_Request request_after,
|
|
MPI_Status* status )
|
|
{
|
|
int recv_count = 0;
|
|
ShadowRequest* shadow;
|
|
/* How do we know if this is an Irecv request that has now
|
|
finished successfully?
|
|
|
|
request_before isn't MPI_REQUEST_NULL
|
|
and request_before is found in the shadow table
|
|
and request_after *is* MPI_REQUEST_NULL
|
|
and (if error_in_status then status.MPI_ERROR is MPI_SUCCESS)
|
|
|
|
(when error_in_status == False, then we expect not to get
|
|
called at all if there was an error.)
|
|
*/
|
|
if (request_before != MPI_REQUEST_NULL
|
|
&& request_after == MPI_REQUEST_NULL
|
|
&& (error_in_status ? status->MPI_ERROR == MPI_SUCCESS : True)
|
|
&& ( (shadow=find_shadow_Request(request_before)) != NULL) ) {
|
|
/* The Irecv detailed in 'shadow' completed. Paint the result
|
|
buffer, and delete the entry. */
|
|
if (count_from_Status(&recv_count, shadow->datatype, status)) {
|
|
make_mem_defined_if_addressable(shadow->buf, recv_count, shadow->datatype);
|
|
if (opt_verbosity > 1)
|
|
fprintf(stderr, "%s %5d: sReq- %p (completed)\n",
|
|
preamble, my_pid, request_before);
|
|
}
|
|
delete_shadow_Request(request_before);
|
|
}
|
|
}
|
|
|
|
|
|
/* --- Isend --- */
|
|
/* rd: (buf,count,datatype) */
|
|
/* wr: *request */
|
|
static __inline__
|
|
int generic_Isend(void *buf, int count, MPI_Datatype datatype,
|
|
int dest, int tag, MPI_Comm comm,
|
|
MPI_Request* request)
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("{,B,S,R}Isend");
|
|
check_mem_is_defined(buf, count, datatype);
|
|
check_mem_is_addressable_untyped(request, sizeof(*request));
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_7W(err, fn, buf,count,datatype,dest,tag,comm,request);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
make_mem_defined_if_addressable_if_success_untyped(err, request, sizeof(*request));
|
|
after("{,B,S,R}Isend", err);
|
|
return err;
|
|
}
|
|
int WRAPPER_FOR(PMPI_Isend)(void *buf, int count, MPI_Datatype datatype,
|
|
int dest, int tag, MPI_Comm comm,
|
|
MPI_Request* request) {
|
|
return generic_Isend(buf,count,datatype, dest,tag,comm, request);
|
|
}
|
|
int WRAPPER_FOR(PMPI_Ibsend)(void *buf, int count, MPI_Datatype datatype,
|
|
int dest, int tag, MPI_Comm comm,
|
|
MPI_Request* request) {
|
|
return generic_Isend(buf,count,datatype, dest,tag,comm, request);
|
|
}
|
|
int WRAPPER_FOR(PMPI_Issend)(void *buf, int count, MPI_Datatype datatype,
|
|
int dest, int tag, MPI_Comm comm,
|
|
MPI_Request* request) {
|
|
return generic_Isend(buf,count,datatype, dest,tag,comm, request);
|
|
}
|
|
int WRAPPER_FOR(PMPI_Irsend)(void *buf, int count, MPI_Datatype datatype,
|
|
int dest, int tag, MPI_Comm comm,
|
|
MPI_Request* request) {
|
|
return generic_Isend(buf,count,datatype, dest,tag,comm, request);
|
|
}
|
|
|
|
|
|
/* --- Irecv --- */
|
|
/* pre: must be writable: (buf,count,datatype), *request
|
|
post: make readable *request
|
|
add a request->(buf,count,ty) binding to the
|
|
shadow request table.
|
|
*/
|
|
int WRAPPER_FOR(PMPI_Irecv)( void* buf, int count, MPI_Datatype datatype,
|
|
int source, int tag, MPI_Comm comm,
|
|
MPI_Request* request )
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Irecv");
|
|
check_mem_is_addressable(buf, count, datatype);
|
|
check_mem_is_addressable_untyped(request, sizeof(*request));
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_7W(err, fn, buf,count,datatype,source,tag,comm,request);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
if (err == MPI_SUCCESS) {
|
|
make_mem_defined_if_addressable_untyped(request, sizeof(*request));
|
|
add_shadow_Request( *request, buf,count,datatype );
|
|
}
|
|
after("Irecv", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Wait --- */
|
|
/* The MPI1 spec (imprecisely) defines 3 request states:
|
|
- "null" if the request is MPI_REQUEST_NULL
|
|
- "inactive" if not "null" and not associated with ongoing comms
|
|
- "active" if not "null" and is associated with ongoing comms
|
|
*/
|
|
int WRAPPER_FOR(PMPI_Wait)( MPI_Request* request,
|
|
MPI_Status* status )
|
|
{
|
|
MPI_Request request_before;
|
|
MPI_Status fake_status;
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Wait");
|
|
if (isMSI(status))
|
|
status = &fake_status;
|
|
check_mem_is_addressable_untyped(status, sizeof(MPI_Status));
|
|
check_mem_is_defined_untyped(request, sizeof(MPI_Request));
|
|
request_before = *request;
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_WW(err, fn, request,status);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
if (err == MPI_SUCCESS) {
|
|
maybe_complete(False/*err in status?*/,
|
|
request_before, *request, status);
|
|
make_mem_defined_if_addressable_untyped(status, sizeof(MPI_Status));
|
|
}
|
|
after("Wait", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Waitany --- */
|
|
int WRAPPER_FOR(PMPI_Waitany)( int count,
|
|
MPI_Request* requests,
|
|
int* index,
|
|
MPI_Status* status )
|
|
{
|
|
MPI_Request* requests_before = NULL;
|
|
MPI_Status fake_status;
|
|
OrigFn fn;
|
|
int err, i;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Waitany");
|
|
if (isMSI(status))
|
|
status = &fake_status;
|
|
if (0) fprintf(stderr, "Waitany: %d\n", count);
|
|
check_mem_is_addressable_untyped(index, sizeof(int));
|
|
check_mem_is_addressable_untyped(status, sizeof(MPI_Status));
|
|
for (i = 0; i < count; i++) {
|
|
check_mem_is_defined_untyped(&requests[i], sizeof(MPI_Request));
|
|
}
|
|
requests_before = clone_Request_array( count, requests );
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_WWWW(err, fn, count,requests,index,status);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
if (err == MPI_SUCCESS && *index >= 0 && *index < count) {
|
|
maybe_complete(False/*err in status?*/,
|
|
requests_before[*index], requests[*index], status);
|
|
make_mem_defined_if_addressable_untyped(status, sizeof(MPI_Status));
|
|
}
|
|
if (requests_before)
|
|
free(requests_before);
|
|
after("Waitany", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Waitall --- */
|
|
int WRAPPER_FOR(PMPI_Waitall)( int count,
|
|
MPI_Request* requests,
|
|
MPI_Status* statuses )
|
|
{
|
|
MPI_Request* requests_before = NULL;
|
|
OrigFn fn;
|
|
int err, i;
|
|
Bool free_sta = False;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Waitall");
|
|
if (0) fprintf(stderr, "Waitall: %d\n", count);
|
|
if (isMSI(statuses)) {
|
|
free_sta = True;
|
|
statuses = malloc( (count < 0 ? 0 : count) * sizeof(MPI_Status) );
|
|
}
|
|
for (i = 0; i < count; i++) {
|
|
check_mem_is_addressable_untyped(&statuses[i], sizeof(MPI_Status));
|
|
check_mem_is_defined_untyped(&requests[i], sizeof(MPI_Request));
|
|
}
|
|
requests_before = clone_Request_array( count, requests );
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_WWW(err, fn, count,requests,statuses);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
if (err == MPI_SUCCESS /*complete success*/
|
|
|| err == MPI_ERR_IN_STATUS /* partial success */) {
|
|
Bool e_i_s = err == MPI_ERR_IN_STATUS;
|
|
for (i = 0; i < count; i++) {
|
|
maybe_complete(e_i_s, requests_before[i], requests[i],
|
|
&statuses[i]);
|
|
make_mem_defined_if_addressable_untyped(&statuses[i],
|
|
sizeof(MPI_Status));
|
|
}
|
|
}
|
|
if (requests_before)
|
|
free(requests_before);
|
|
if (free_sta)
|
|
free(statuses);
|
|
after("Waitall", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Test --- */
|
|
/* nonblocking version of Wait */
|
|
int WRAPPER_FOR(PMPI_Test)( MPI_Request* request, int* flag,
|
|
MPI_Status* status )
|
|
{
|
|
MPI_Request request_before;
|
|
MPI_Status fake_status;
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Test");
|
|
if (isMSI(status))
|
|
status = &fake_status;
|
|
check_mem_is_addressable_untyped(status, sizeof(MPI_Status));
|
|
check_mem_is_addressable_untyped(flag, sizeof(int));
|
|
check_mem_is_defined_untyped(request, sizeof(MPI_Request));
|
|
request_before = *request;
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_WWW(err, fn, request,flag,status);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
if (err == MPI_SUCCESS && *flag) {
|
|
maybe_complete(False/*err in status?*/,
|
|
request_before, *request, status);
|
|
make_mem_defined_if_addressable_untyped(status, sizeof(MPI_Status));
|
|
}
|
|
after("Test", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Testall --- */
|
|
/* nonblocking version of Waitall */
|
|
int WRAPPER_FOR(PMPI_Testall)( int count, MPI_Request* requests,
|
|
int* flag, MPI_Status* statuses )
|
|
{
|
|
MPI_Request* requests_before = NULL;
|
|
OrigFn fn;
|
|
int err, i;
|
|
Bool free_sta = False;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Testall");
|
|
if (0) fprintf(stderr, "Testall: %d\n", count);
|
|
if (isMSI(statuses)) {
|
|
free_sta = True;
|
|
statuses = malloc( (count < 0 ? 0 : count) * sizeof(MPI_Status) );
|
|
}
|
|
check_mem_is_addressable_untyped(flag, sizeof(int));
|
|
for (i = 0; i < count; i++) {
|
|
check_mem_is_addressable_untyped(&statuses[i], sizeof(MPI_Status));
|
|
check_mem_is_defined_untyped(&requests[i], sizeof(MPI_Request));
|
|
}
|
|
requests_before = clone_Request_array( count, requests );
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_WWWW(err, fn, count,requests,flag,statuses);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
/* Urk. Is the following "if (...)" really right? I don't know. */
|
|
if (*flag
|
|
&& (err == MPI_SUCCESS /*complete success*/
|
|
|| err == MPI_ERR_IN_STATUS /* partial success */)) {
|
|
Bool e_i_s = err == MPI_ERR_IN_STATUS;
|
|
for (i = 0; i < count; i++) {
|
|
maybe_complete(e_i_s, requests_before[i], requests[i],
|
|
&statuses[i]);
|
|
make_mem_defined_if_addressable_untyped(&statuses[i],
|
|
sizeof(MPI_Status));
|
|
}
|
|
}
|
|
if (requests_before)
|
|
free(requests_before);
|
|
if (free_sta)
|
|
free(statuses);
|
|
after("Testall", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Iprobe --- */
|
|
/* pre: must-be-writable: *flag, *status */
|
|
/* post: make-readable *flag
|
|
if *flag==True make-defined *status */
|
|
int WRAPPER_FOR(PMPI_Iprobe)(int source, int tag,
|
|
MPI_Comm comm,
|
|
int* flag, MPI_Status* status)
|
|
{
|
|
MPI_Status fake_status;
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Iprobe");
|
|
if (isMSI(status))
|
|
status = &fake_status;
|
|
check_mem_is_addressable_untyped(flag, sizeof(*flag));
|
|
check_mem_is_addressable_untyped(status, sizeof(*status));
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_5W(err, fn, source,tag,comm,flag,status);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
if (err == MPI_SUCCESS) {
|
|
make_mem_defined_if_addressable_untyped(flag, sizeof(*flag));
|
|
if (*flag)
|
|
make_mem_defined_if_addressable_untyped(status, sizeof(*status));
|
|
}
|
|
after("Iprobe", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Probe --- */
|
|
/* pre: must-be-writable *status */
|
|
/* post: make-defined *status */
|
|
int WRAPPER_FOR(PMPI_Probe)(int source, int tag,
|
|
MPI_Comm comm, MPI_Status* status)
|
|
{
|
|
MPI_Status fake_status;
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Probe");
|
|
if (isMSI(status))
|
|
status = &fake_status;
|
|
check_mem_is_addressable_untyped(status, sizeof(*status));
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_WWWW(err, fn, source,tag,comm,status);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
make_mem_defined_if_addressable_if_success_untyped(err, status, sizeof(*status));
|
|
after("Probe", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Cancel --- */
|
|
/* Wrapping PMPI_Cancel is interesting only to the extent that we need
|
|
to be able to detect when a request should be removed from our
|
|
shadow table due to cancellation. */
|
|
int WRAPPER_FOR(PMPI_Cancel)(MPI_Request* request)
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
MPI_Request tmp;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Cancel");
|
|
check_mem_is_addressable_untyped(request, sizeof(*request));
|
|
tmp = *request;
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_W(err, fn, request);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
if (err == MPI_SUCCESS)
|
|
delete_shadow_Request(tmp);
|
|
after("Cancel", err);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Sec 3.10, Send-receive ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* --- Sendrecv --- */
|
|
/* pre: must be readable: (sendbuf,sendcount,sendtype)
|
|
must be writable: (recvbuf,recvcount,recvtype)
|
|
post: make readable: (recvbuf,recvcount_actual,datatype)
|
|
where recvcount_actual is determined from *status
|
|
*/
|
|
int WRAPPER_FOR(PMPI_Sendrecv)(
|
|
void *sendbuf, int sendcount, MPI_Datatype sendtype,
|
|
int dest, int sendtag,
|
|
void *recvbuf, int recvcount, MPI_Datatype recvtype,
|
|
int source, int recvtag,
|
|
MPI_Comm comm, MPI_Status *status)
|
|
{
|
|
MPI_Status fake_status;
|
|
OrigFn fn;
|
|
int err, recvcount_actual = 0;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Sendrecv");
|
|
if (isMSI(status))
|
|
status = &fake_status;
|
|
check_mem_is_defined(sendbuf, sendcount, sendtype);
|
|
check_mem_is_addressable(recvbuf, recvcount, recvtype);
|
|
check_mem_is_addressable_untyped(status, sizeof(*status));
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_12W(err, fn, sendbuf,sendcount,sendtype,dest,sendtag,
|
|
recvbuf,recvcount,recvtype,source,recvtag,
|
|
comm,status);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
make_mem_defined_if_addressable_if_success_untyped(err, status, sizeof(*status));
|
|
if (err == MPI_SUCCESS
|
|
&& count_from_Status(&recvcount_actual,recvtype,status)) {
|
|
make_mem_defined_if_addressable(recvbuf, recvcount_actual, recvtype);
|
|
}
|
|
after("Sendrecv", err);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Sec 3.12, Derived datatypes ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* --- Address --- */
|
|
/* Does this have anything worth checking? */
|
|
HAS_NO_WRAPPER(Address)
|
|
|
|
/* --- MPI 2 stuff --- */
|
|
/* Type_extent, Type_get_contents and Type_get_envelope sometimes get
|
|
used intensively by the type walker (walk_type). There's no reason
|
|
why they couldn't be properly wrapped if needed, but doing so slows
|
|
everything down, so don't bother until needed. */
|
|
HAS_NO_WRAPPER(Type_extent)
|
|
HAS_NO_WRAPPER(Type_get_contents)
|
|
HAS_NO_WRAPPER(Type_get_envelope)
|
|
|
|
/* --- Type_commit --- */
|
|
int WRAPPER_FOR(PMPI_Type_commit)( MPI_Datatype* ty )
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Type_commit");
|
|
check_mem_is_defined_untyped(ty, sizeof(*ty));
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_W(err, fn, ty);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
after("Type_commit", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Type_free --- */
|
|
int WRAPPER_FOR(PMPI_Type_free)( MPI_Datatype* ty )
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Type_free");
|
|
check_mem_is_defined_untyped(ty, sizeof(*ty));
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_W(err, fn, ty);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
after("Type_free", err);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Sec 3.13, Pack and unpack ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* --- Pack --- */
|
|
/* pre: must be readable: position
|
|
must be readable: (inbuf,incount,datatype)
|
|
must be writable: outbuf[0 .. outsize-1]
|
|
must be writable: outbuf[*position ..
|
|
*position - 1
|
|
+ however much space PMPI_Pack_size
|
|
says we will need]
|
|
post: make readable: outbuf[old *position .. new *position]
|
|
*/
|
|
int WRAPPER_FOR(PMPI_Pack)( void* inbuf, int incount, MPI_Datatype datatype,
|
|
void* outbuf, int outsize,
|
|
int* position, MPI_Comm comm )
|
|
{
|
|
OrigFn fn;
|
|
int err, szB = 0;
|
|
int position_ORIG = *position;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Pack");
|
|
/* stay sane */
|
|
check_mem_is_defined_untyped(position, sizeof(*position));
|
|
/* check input */
|
|
check_mem_is_defined(inbuf, incount, datatype);
|
|
/* check output area's stated bounds make sense */
|
|
check_mem_is_addressable_untyped(outbuf, outsize);
|
|
/* check output area's actual used size properly */
|
|
err = PMPI_Pack_size( incount, datatype, comm, &szB );
|
|
if (err == MPI_SUCCESS && szB > 0) {
|
|
check_mem_is_addressable_untyped(
|
|
((char*)outbuf) + position_ORIG, szB
|
|
);
|
|
}
|
|
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_7W(err, fn, inbuf,incount,datatype, outbuf,outsize,position, comm);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
|
|
if (err == MPI_SUCCESS && (*position) > position_ORIG) {
|
|
/* paint output */
|
|
make_mem_defined_if_addressable_untyped(
|
|
((char*)outbuf) + position_ORIG, *position - position_ORIG
|
|
);
|
|
}
|
|
after("Pack", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Unpack --- */
|
|
/* pre: must be readable: position
|
|
must be writable: (outbuf,outcount,datatype)
|
|
must be writable: outbuf[0 .. outsize-1]
|
|
must be writable: outbuf[*position ..
|
|
*position - 1
|
|
+ however much space PMPI_Pack_size
|
|
says we will need]
|
|
post: make readable: (outbuf,outcount,datatype)
|
|
and also do a readability check of
|
|
inbuf[old *position .. new *position]
|
|
*/
|
|
int WRAPPER_FOR(PMPI_Unpack)( void* inbuf, int insize, int* position,
|
|
void* outbuf, int outcount, MPI_Datatype datatype,
|
|
MPI_Comm comm )
|
|
{
|
|
OrigFn fn;
|
|
int err, szB = 0;
|
|
int position_ORIG = *position;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Unpack");
|
|
/* stay sane */
|
|
check_mem_is_defined_untyped(position, sizeof(*position));
|
|
/* check output area is accessible */
|
|
check_mem_is_addressable(outbuf, outcount, datatype);
|
|
/* check input area's stated bounds make sense */
|
|
check_mem_is_addressable_untyped(inbuf, insize);
|
|
/* check input area's actual used size properly */
|
|
err = PMPI_Pack_size( outcount, datatype, comm, &szB );
|
|
if (err == MPI_SUCCESS && szB > 0) {
|
|
check_mem_is_addressable_untyped(
|
|
((char*)inbuf) + position_ORIG, szB
|
|
);
|
|
}
|
|
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_7W(err, fn, inbuf,insize,position, outbuf,outcount,datatype, comm);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
|
|
if (err == MPI_SUCCESS && (*position) > position_ORIG) {
|
|
/* recheck input more carefully */
|
|
check_mem_is_defined_untyped(
|
|
((char*)inbuf) + position_ORIG, *position - position_ORIG
|
|
);
|
|
/* paint output */
|
|
make_mem_defined_if_addressable( outbuf, outcount, datatype );
|
|
}
|
|
after("Unpack", err);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Sec 4.4, Broadcast ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* --- Bcast --- */
|
|
/* pre: must-be-readable (buffer,count,datatype) for rank==root
|
|
must-be-writable (buffer,count,datatype) for rank!=root
|
|
post: make-readable (buffer,count,datatype) for all
|
|
|
|
Resulting behaviour is: if root sends uninitialised stuff, then
|
|
V complains, but then all ranks, including itself, see the buffer
|
|
as initialised after that.
|
|
*/
|
|
int WRAPPER_FOR(PMPI_Bcast)(void *buffer, int count,
|
|
MPI_Datatype datatype,
|
|
int root, MPI_Comm comm)
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
Bool i_am_sender;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Bcast");
|
|
i_am_sender = root == comm_rank(comm);
|
|
if (i_am_sender) {
|
|
check_mem_is_defined(buffer, count, datatype);
|
|
} else {
|
|
check_mem_is_addressable(buffer, count, datatype);
|
|
}
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_5W(err, fn, buffer,count,datatype,root,comm);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
make_mem_defined_if_addressable_if_success(err, buffer, count, datatype);
|
|
after("Bcast", err);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Sec 4.5, Gather ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* --- Gather --- */
|
|
/* JRS 20060217: I don't really understand this. Each process is
|
|
going to send sendcount items of type sendtype to the root. So
|
|
the root is going to receive comm_size*sendcount items of type
|
|
sendtype (right?) So why specify recvcount and recvtype?
|
|
|
|
Anyway, assuming the MPI Spec is correct (seems likely :-) we have:
|
|
|
|
pre: (all) must be readable: (sendbuf,sendcount,sendtype)
|
|
(root only): must be writable: (recvbuf,recvcount * comm_size,recvtype)
|
|
post: (root only): make readable: (recvbuf,recvcount * comm_size,recvtype)
|
|
*/
|
|
int WRAPPER_FOR(PMPI_Gather)(
|
|
void *sendbuf, int sendcount, MPI_Datatype sendtype,
|
|
void *recvbuf, int recvcount, MPI_Datatype recvtype,
|
|
int root, MPI_Comm comm)
|
|
{
|
|
OrigFn fn;
|
|
int err, me, sz;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Gather");
|
|
me = comm_rank(comm);
|
|
sz = comm_size(comm);
|
|
check_mem_is_defined(sendbuf, sendcount, sendtype);
|
|
if (me == root)
|
|
check_mem_is_addressable(recvbuf, recvcount * sz, recvtype);
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_8W(err, fn, sendbuf,sendcount,sendtype,
|
|
recvbuf,recvcount,recvtype,
|
|
root,comm);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
if (me == root)
|
|
make_mem_defined_if_addressable_if_success(err, recvbuf, recvcount * sz, recvtype);
|
|
after("Gather", err);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Sec 4.6, Scatter ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* pre: (root only): must be readable: (sendbuf,sendcount * comm_size,sendtype)
|
|
(all): must be writable: (recvbuf,recvbuf,recvtype)
|
|
post: (all): make defined: (recvbuf,recvbuf,recvtype)
|
|
*/
|
|
int WRAPPER_FOR(PMPI_Scatter)(
|
|
void* sendbuf, int sendcount, MPI_Datatype sendtype,
|
|
void* recvbuf, int recvcount, MPI_Datatype recvtype,
|
|
int root, MPI_Comm comm)
|
|
{
|
|
OrigFn fn;
|
|
int err, me, sz;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Scatter");
|
|
me = comm_rank(comm);
|
|
sz = comm_size(comm);
|
|
check_mem_is_addressable(recvbuf, recvcount, recvtype);
|
|
if (me == root)
|
|
check_mem_is_defined(sendbuf, sendcount * sz, sendtype);
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_8W(err, fn, sendbuf,sendcount,sendtype,
|
|
recvbuf,recvcount,recvtype,
|
|
root,comm);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
make_mem_defined_if_addressable_if_success(err, recvbuf, recvcount, recvtype);
|
|
after("Scatter", err);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Sec 4.8, All-to-All Scatter/Gather ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* pre: (all) must be readable: (sendbuf,sendcount * comm_size,sendtype)
|
|
(all) must be writable: (recvbuf,recvcount * comm_size,recvtype)
|
|
post: (all) make defined: (recvbuf,recvcount * comm_size,recvtype)
|
|
*/
|
|
int WRAPPER_FOR(PMPI_Alltoall)(
|
|
void* sendbuf, int sendcount, MPI_Datatype sendtype,
|
|
void* recvbuf, int recvcount, MPI_Datatype recvtype,
|
|
MPI_Comm comm)
|
|
{
|
|
OrigFn fn;
|
|
int err, sz;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Alltoall");
|
|
sz = comm_size(comm);
|
|
check_mem_is_defined(sendbuf, sendcount * sz, sendtype);
|
|
check_mem_is_addressable(recvbuf, recvcount * sz, recvtype);
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_7W(err, fn, sendbuf,sendcount,sendtype,
|
|
recvbuf,recvcount,recvtype,
|
|
comm);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
make_mem_defined_if_addressable_if_success(err, recvbuf, recvcount * sz, recvtype);
|
|
after("Alltoall", err);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Sec 4.9, Global Reduction Operations ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* --- Reduce --- */
|
|
/* rd: (sendbuf,count,datatype) for all
|
|
wr: (recvbuf,count,datatype) but only for rank == root
|
|
*/
|
|
int WRAPPER_FOR(PMPI_Reduce)(void *sendbuf, void *recvbuf,
|
|
int count,
|
|
MPI_Datatype datatype, MPI_Op op,
|
|
int root, MPI_Comm comm)
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
Bool i_am_root;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Reduce");
|
|
i_am_root = root == comm_rank(comm);
|
|
check_mem_is_defined(sendbuf, count, datatype);
|
|
if (i_am_root)
|
|
check_mem_is_addressable(recvbuf, count, datatype);
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_7W(err, fn, sendbuf,recvbuf,count,datatype,op,root,comm);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
if (i_am_root)
|
|
make_mem_defined_if_addressable_if_success(err, recvbuf, count, datatype);
|
|
after("Reduce", err);
|
|
return err;
|
|
}
|
|
|
|
|
|
/* --- Allreduce --- */
|
|
/* rd: (sendbuf,count,datatype) for all
|
|
wr: (recvbuf,count,datatype) for all
|
|
*/
|
|
int WRAPPER_FOR(PMPI_Allreduce)(void *sendbuf, void *recvbuf,
|
|
int count,
|
|
MPI_Datatype datatype, MPI_Op op,
|
|
MPI_Comm comm)
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Allreduce");
|
|
check_mem_is_defined(sendbuf, count, datatype);
|
|
check_mem_is_addressable(recvbuf, count, datatype);
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_6W(err, fn, sendbuf,recvbuf,count,datatype,op,comm);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
make_mem_defined_if_addressable_if_success(err, recvbuf, count, datatype);
|
|
after("Allreduce", err);
|
|
return err;
|
|
}
|
|
|
|
|
|
/* --- Op_create --- */
|
|
/* This is a bit dubious. I suppose it takes 'function' and
|
|
writes something at *op, but who knows what an MPI_Op is?
|
|
Can we safely do 'sizeof' on it? */
|
|
int WRAPPER_FOR(PMPI_Op_create)( MPI_User_function* function,
|
|
int commute,
|
|
MPI_Op* op )
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Op_create");
|
|
check_mem_is_addressable_untyped(op, sizeof(*op));
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_WWW(err, fn, function,commute,op);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
make_mem_defined_if_addressable_if_success_untyped(err, op, sizeof(*op));
|
|
after("Op_create", err);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Sec 5.4, Communicator management ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* Hardly seems worth wrapping Comm_rank and Comm_size, but
|
|
since it's done now .. */
|
|
|
|
/* --- Comm_create --- */
|
|
/* Let normal memcheck tracking handle this. */
|
|
int WRAPPER_FOR(PMPI_Comm_create)(MPI_Comm comm, MPI_Group group,
|
|
MPI_Comm* newcomm)
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Comm_create");
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_WWW(err, fn, comm,group,newcomm);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
after("Comm_create", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Comm_dup --- */
|
|
/* Let normal memcheck tracking handle this. */
|
|
int WRAPPER_FOR(PMPI_Comm_dup)(MPI_Comm comm, MPI_Comm* newcomm)
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Comm_dup");
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_WW(err, fn, comm,newcomm);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
after("Comm_dup", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Comm_free --- */
|
|
/* Let normal memcheck tracking handle this. */
|
|
int WRAPPER_FOR(PMPI_Comm_free)(MPI_Comm* comm)
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Comm_free");
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_W(err, fn, comm);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
after("Comm_free", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Comm_rank --- */
|
|
/* wr: (rank, sizeof(*rank)) */
|
|
int WRAPPER_FOR(PMPI_Comm_rank)(MPI_Comm comm, int *rank)
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Comm_rank");
|
|
check_mem_is_addressable_untyped(rank, sizeof(*rank));
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_WW(err, fn, comm,rank);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
make_mem_defined_if_addressable_if_success_untyped(err, rank, sizeof(*rank));
|
|
after("Comm_rank", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Comm_size --- */
|
|
/* wr: (size, sizeof(*size)) */
|
|
int WRAPPER_FOR(PMPI_Comm_size)(MPI_Comm comm, int *size)
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Comm_size");
|
|
check_mem_is_addressable_untyped(size, sizeof(*size));
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_WW(err, fn, comm,size);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
make_mem_defined_if_addressable_if_success_untyped(err, size, sizeof(*size));
|
|
after("Comm_size", err);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Sec 5.7, Caching ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Sec 7.3, Error codes and classes ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* --- Error_string --- */
|
|
int WRAPPER_FOR(PMPI_Error_string)( int errorcode, char* string,
|
|
int* resultlen )
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Error_string");
|
|
check_mem_is_addressable_untyped(resultlen, sizeof(int));
|
|
check_mem_is_addressable_untyped(string, MPI_MAX_ERROR_STRING);
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_WWW(err, fn, errorcode,string,resultlen);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
/* Don't bother to paint the result; we assume the real function
|
|
will have filled it with defined characters :-) */
|
|
after("Error_string", err);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Sec 7.5, Startup ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* --- Init --- */
|
|
/* rd: *argc, *argv[0 .. *argc-1] */
|
|
long WRAPPER_FOR(PMPI_Init)(int *argc, char ***argv)
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Init");
|
|
if (argc) {
|
|
check_mem_is_defined_untyped(argc, sizeof(int));
|
|
}
|
|
if (argc && argv) {
|
|
check_mem_is_defined_untyped(*argv, *argc * sizeof(char**));
|
|
}
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_WW(err, fn, argc,argv);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
after("Init", err);
|
|
if (opt_initkludge)
|
|
return (long)(void*)&mpiwrap_walk_type_EXTERNALLY_VISIBLE;
|
|
else
|
|
return (long)err;
|
|
}
|
|
|
|
/* --- Initialized --- */
|
|
int WRAPPER_FOR(PMPI_Initialized)(int* flag)
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Initialized");
|
|
check_mem_is_addressable_untyped(flag, sizeof(int));
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_W(err, fn, flag);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
make_mem_defined_if_addressable_if_success_untyped(err, flag, sizeof(int));
|
|
after("Initialized", err);
|
|
return err;
|
|
}
|
|
|
|
/* --- Finalize --- */
|
|
int WRAPPER_FOR(PMPI_Finalize)(void)
|
|
{
|
|
OrigFn fn;
|
|
int err;
|
|
VALGRIND_GET_ORIG_FN(fn);
|
|
before("Finalize");
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
|
|
CALL_FN_W_v(err, fn);
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
|
|
after("Finalize", err);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- Default wrappers for all remaining functions ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/* Boilerplate for default wrappers. */
|
|
#define DEFAULT_WRAPPER_PREAMBLE(basename) \
|
|
OrigFn fn; \
|
|
UWord res; \
|
|
static int complaints = 1; \
|
|
VALGRIND_GET_ORIG_FN(fn); \
|
|
before(#basename); \
|
|
if (opt_missing >= 2) { \
|
|
barf("no wrapper for PMPI_" #basename \
|
|
",\n\t\t\t and you have " \
|
|
"requested strict checking"); \
|
|
} \
|
|
if (opt_missing == 1 && complaints > 0) { \
|
|
fprintf(stderr, "%s %5d: warning: no wrapper " \
|
|
"for PMPI_" #basename "\n", \
|
|
preamble, my_pid); \
|
|
complaints--; \
|
|
} \
|
|
|
|
#define DEFAULT_WRAPPER_W_0W(basename) \
|
|
UWord WRAPPER_FOR(PMPI_##basename)( void ) \
|
|
{ \
|
|
DEFAULT_WRAPPER_PREAMBLE(basename) \
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING; \
|
|
CALL_FN_W_v(res, fn); \
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING; \
|
|
return res; \
|
|
}
|
|
|
|
#define DEFAULT_WRAPPER_W_1W(basename) \
|
|
UWord WRAPPER_FOR(PMPI_##basename)( UWord a1 ) \
|
|
{ \
|
|
DEFAULT_WRAPPER_PREAMBLE(basename) \
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING; \
|
|
CALL_FN_W_W(res, fn, a1); \
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING; \
|
|
return res; \
|
|
}
|
|
|
|
#define DEFAULT_WRAPPER_W_2W(basename) \
|
|
UWord WRAPPER_FOR(PMPI_##basename)( UWord a1, UWord a2 ) \
|
|
{ \
|
|
DEFAULT_WRAPPER_PREAMBLE(basename) \
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING; \
|
|
CALL_FN_W_WW(res, fn, a1,a2); \
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING; \
|
|
return res; \
|
|
}
|
|
|
|
#define DEFAULT_WRAPPER_W_3W(basename) \
|
|
UWord WRAPPER_FOR(PMPI_##basename) \
|
|
( UWord a1, UWord a2, UWord a3 ) \
|
|
{ \
|
|
DEFAULT_WRAPPER_PREAMBLE(basename) \
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING; \
|
|
CALL_FN_W_WWW(res, fn, a1,a2,a3); \
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING; \
|
|
return res; \
|
|
}
|
|
|
|
#define DEFAULT_WRAPPER_W_4W(basename) \
|
|
UWord WRAPPER_FOR(PMPI_##basename) \
|
|
( UWord a1, UWord a2, UWord a3, UWord a4 ) \
|
|
{ \
|
|
DEFAULT_WRAPPER_PREAMBLE(basename) \
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING; \
|
|
CALL_FN_W_WWWW(res, fn, a1,a2,a3,a4); \
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING; \
|
|
return res; \
|
|
}
|
|
|
|
#define DEFAULT_WRAPPER_W_5W(basename) \
|
|
UWord WRAPPER_FOR(PMPI_##basename) \
|
|
( UWord a1, UWord a2, UWord a3, UWord a4, UWord a5 ) \
|
|
{ \
|
|
DEFAULT_WRAPPER_PREAMBLE(basename) \
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING; \
|
|
CALL_FN_W_5W(res, fn, a1,a2,a3,a4,a5); \
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING; \
|
|
return res; \
|
|
}
|
|
|
|
#define DEFAULT_WRAPPER_W_6W(basename) \
|
|
UWord WRAPPER_FOR(PMPI_##basename) \
|
|
( UWord a1, UWord a2, UWord a3, UWord a4, UWord a5, \
|
|
UWord a6 ) \
|
|
{ \
|
|
DEFAULT_WRAPPER_PREAMBLE(basename) \
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING; \
|
|
CALL_FN_W_6W(res, fn, a1,a2,a3,a4,a5,a6); \
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING; \
|
|
return res; \
|
|
}
|
|
|
|
#define DEFAULT_WRAPPER_W_7W(basename) \
|
|
UWord WRAPPER_FOR(PMPI_##basename) \
|
|
( UWord a1, UWord a2, UWord a3, UWord a4, UWord a5, \
|
|
UWord a6, UWord a7 ) \
|
|
{ \
|
|
DEFAULT_WRAPPER_PREAMBLE(basename) \
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING; \
|
|
CALL_FN_W_7W(res, fn, a1,a2,a3,a4,a5,a6,a7); \
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING; \
|
|
return res; \
|
|
}
|
|
|
|
#define DEFAULT_WRAPPER_W_8W(basename) \
|
|
UWord WRAPPER_FOR(PMPI_##basename) \
|
|
( UWord a1, UWord a2, UWord a3, UWord a4, UWord a5, \
|
|
UWord a6, UWord a7, UWord a8 ) \
|
|
{ \
|
|
DEFAULT_WRAPPER_PREAMBLE(basename) \
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING; \
|
|
CALL_FN_W_8W(res, fn, a1,a2,a3,a4,a5,a6,a7,a8); \
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING; \
|
|
return res; \
|
|
}
|
|
|
|
#define DEFAULT_WRAPPER_W_9W(basename) \
|
|
UWord WRAPPER_FOR(PMPI_##basename) \
|
|
( UWord a1, UWord a2, UWord a3, UWord a4, UWord a5, \
|
|
UWord a6, UWord a7, UWord a8, UWord a9 ) \
|
|
{ \
|
|
DEFAULT_WRAPPER_PREAMBLE(basename) \
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING; \
|
|
CALL_FN_W_9W(res, fn, a1,a2,a3,a4,a5,a6,a7,a8,a9); \
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING; \
|
|
return res; \
|
|
}
|
|
|
|
#define DEFAULT_WRAPPER_W_10W(basename) \
|
|
UWord WRAPPER_FOR(PMPI_##basename) \
|
|
( UWord a1, UWord a2, UWord a3, UWord a4, UWord a5, \
|
|
UWord a6, UWord a7, UWord a8, UWord a9, UWord a10 ) \
|
|
{ \
|
|
DEFAULT_WRAPPER_PREAMBLE(basename) \
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING; \
|
|
CALL_FN_W_10W(res, fn, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10); \
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING; \
|
|
return res; \
|
|
}
|
|
|
|
#define DEFAULT_WRAPPER_W_12W(basename) \
|
|
UWord WRAPPER_FOR(PMPI_##basename) \
|
|
( UWord a1, UWord a2, UWord a3, UWord a4, UWord a5, \
|
|
UWord a6, UWord a7, UWord a8, UWord a9, UWord a10, \
|
|
UWord a11, UWord a12 ) \
|
|
{ \
|
|
DEFAULT_WRAPPER_PREAMBLE(basename) \
|
|
if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING; \
|
|
CALL_FN_W_12W(res, fn, a1,a2,a3,a4,a5,a6, \
|
|
a7,a8,a9,a10,a11,a12); \
|
|
if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING; \
|
|
return res; \
|
|
}
|
|
|
|
|
|
/* Canned summary of MPI-1.1/MPI-2 entry points, as derived from mpi.h
|
|
from Open MPI svn rev 9191 (somewhere between Open MPI versions
|
|
1.0.1 and 1.1.0). */
|
|
|
|
/* If a function is commented out in this list, it's because it has a
|
|
proper wrapper written elsewhere (above here). */
|
|
|
|
DEFAULT_WRAPPER_W_2W(Abort)
|
|
DEFAULT_WRAPPER_W_9W(Accumulate)
|
|
DEFAULT_WRAPPER_W_1W(Add_error_class)
|
|
DEFAULT_WRAPPER_W_2W(Add_error_code)
|
|
DEFAULT_WRAPPER_W_2W(Add_error_string)
|
|
/* DEFAULT_WRAPPER_W_2W(Address) */
|
|
DEFAULT_WRAPPER_W_7W(Allgather)
|
|
DEFAULT_WRAPPER_W_8W(Allgatherv)
|
|
DEFAULT_WRAPPER_W_3W(Alloc_mem)
|
|
/* DEFAULT_WRAPPER_W_6W(Allreduce) */
|
|
/* DEFAULT_WRAPPER_W_7W(Alltoall) */
|
|
DEFAULT_WRAPPER_W_9W(Alltoallv)
|
|
DEFAULT_WRAPPER_W_9W(Alltoallw)
|
|
DEFAULT_WRAPPER_W_2W(Attr_delete)
|
|
DEFAULT_WRAPPER_W_4W(Attr_get)
|
|
DEFAULT_WRAPPER_W_3W(Attr_put)
|
|
DEFAULT_WRAPPER_W_1W(Barrier)
|
|
/* DEFAULT_WRAPPER_W_5W(Bcast) */
|
|
/* DEFAULT_WRAPPER_W_6W(Bsend) */
|
|
DEFAULT_WRAPPER_W_7W(Bsend_init)
|
|
DEFAULT_WRAPPER_W_2W(Buffer_attach)
|
|
DEFAULT_WRAPPER_W_2W(Buffer_detach)
|
|
/* DEFAULT_WRAPPER_W_1W(Cancel) */
|
|
DEFAULT_WRAPPER_W_4W(Cart_coords)
|
|
DEFAULT_WRAPPER_W_6W(Cart_create)
|
|
DEFAULT_WRAPPER_W_5W(Cart_get)
|
|
DEFAULT_WRAPPER_W_5W(Cart_map)
|
|
DEFAULT_WRAPPER_W_3W(Cart_rank)
|
|
DEFAULT_WRAPPER_W_5W(Cart_shift)
|
|
DEFAULT_WRAPPER_W_3W(Cart_sub)
|
|
DEFAULT_WRAPPER_W_2W(Cartdim_get)
|
|
DEFAULT_WRAPPER_W_1W(Close_port)
|
|
DEFAULT_WRAPPER_W_5W(Comm_accept)
|
|
DEFAULT_WRAPPER_W_1W(Comm_c2f)
|
|
DEFAULT_WRAPPER_W_2W(Comm_call_errhandler)
|
|
DEFAULT_WRAPPER_W_3W(Comm_compare)
|
|
DEFAULT_WRAPPER_W_5W(Comm_connect)
|
|
DEFAULT_WRAPPER_W_2W(Comm_create_errhandler)
|
|
DEFAULT_WRAPPER_W_4W(Comm_create_keyval)
|
|
/* DEFAULT_WRAPPER_W_3W(Comm_create) */
|
|
DEFAULT_WRAPPER_W_2W(Comm_delete_attr)
|
|
DEFAULT_WRAPPER_W_1W(Comm_disconnect)
|
|
/* DEFAULT_WRAPPER_W_2W(Comm_dup) */
|
|
DEFAULT_WRAPPER_W_1W(Comm_f2c)
|
|
DEFAULT_WRAPPER_W_1W(Comm_free_keyval)
|
|
/* DEFAULT_WRAPPER_W_1W(Comm_free) */
|
|
DEFAULT_WRAPPER_W_4W(Comm_get_attr)
|
|
DEFAULT_WRAPPER_W_2W(Comm_get_errhandler)
|
|
DEFAULT_WRAPPER_W_3W(Comm_get_name)
|
|
DEFAULT_WRAPPER_W_1W(Comm_get_parent)
|
|
DEFAULT_WRAPPER_W_2W(Comm_group)
|
|
DEFAULT_WRAPPER_W_2W(Comm_join)
|
|
/* DEFAULT_WRAPPER_W_2W(Comm_rank) */
|
|
DEFAULT_WRAPPER_W_2W(Comm_remote_group)
|
|
DEFAULT_WRAPPER_W_2W(Comm_remote_size)
|
|
DEFAULT_WRAPPER_W_3W(Comm_set_attr)
|
|
DEFAULT_WRAPPER_W_2W(Comm_set_errhandler)
|
|
DEFAULT_WRAPPER_W_2W(Comm_set_name)
|
|
/* DEFAULT_WRAPPER_W_2W(Comm_size) */
|
|
DEFAULT_WRAPPER_W_8W(Comm_spawn)
|
|
DEFAULT_WRAPPER_W_9W(Comm_spawn_multiple)
|
|
DEFAULT_WRAPPER_W_4W(Comm_split)
|
|
DEFAULT_WRAPPER_W_2W(Comm_test_inter)
|
|
DEFAULT_WRAPPER_W_3W(Dims_create)
|
|
DEFAULT_WRAPPER_W_1W(Errhandler_c2f)
|
|
DEFAULT_WRAPPER_W_2W(Errhandler_create)
|
|
DEFAULT_WRAPPER_W_1W(Errhandler_f2c)
|
|
DEFAULT_WRAPPER_W_1W(Errhandler_free)
|
|
DEFAULT_WRAPPER_W_2W(Errhandler_get)
|
|
DEFAULT_WRAPPER_W_2W(Errhandler_set)
|
|
DEFAULT_WRAPPER_W_2W(Error_class)
|
|
/* DEFAULT_WRAPPER_W_3W(Error_string) */
|
|
DEFAULT_WRAPPER_W_6W(Exscan)
|
|
DEFAULT_WRAPPER_W_1W(File_c2f)
|
|
DEFAULT_WRAPPER_W_1W(File_f2c)
|
|
DEFAULT_WRAPPER_W_2W(File_call_errhandler)
|
|
DEFAULT_WRAPPER_W_2W(File_create_errhandler)
|
|
DEFAULT_WRAPPER_W_2W(File_set_errhandler)
|
|
DEFAULT_WRAPPER_W_2W(File_get_errhandler)
|
|
DEFAULT_WRAPPER_W_5W(File_open)
|
|
DEFAULT_WRAPPER_W_1W(File_close)
|
|
DEFAULT_WRAPPER_W_2W(File_delete)
|
|
DEFAULT_WRAPPER_W_2W(File_set_size)
|
|
DEFAULT_WRAPPER_W_2W(File_preallocate)
|
|
DEFAULT_WRAPPER_W_2W(File_get_size)
|
|
DEFAULT_WRAPPER_W_2W(File_get_group)
|
|
DEFAULT_WRAPPER_W_2W(File_get_amode)
|
|
DEFAULT_WRAPPER_W_2W(File_set_info)
|
|
DEFAULT_WRAPPER_W_2W(File_get_info)
|
|
DEFAULT_WRAPPER_W_6W(File_set_view)
|
|
DEFAULT_WRAPPER_W_5W(File_get_view)
|
|
DEFAULT_WRAPPER_W_6W(File_read_at)
|
|
DEFAULT_WRAPPER_W_6W(File_read_at_all)
|
|
DEFAULT_WRAPPER_W_6W(File_write_at)
|
|
DEFAULT_WRAPPER_W_6W(File_write_at_all)
|
|
DEFAULT_WRAPPER_W_6W(File_iread_at)
|
|
DEFAULT_WRAPPER_W_6W(File_iwrite_at)
|
|
DEFAULT_WRAPPER_W_5W(File_read)
|
|
DEFAULT_WRAPPER_W_5W(File_read_all)
|
|
DEFAULT_WRAPPER_W_5W(File_write)
|
|
DEFAULT_WRAPPER_W_5W(File_write_all)
|
|
DEFAULT_WRAPPER_W_5W(File_iread)
|
|
DEFAULT_WRAPPER_W_5W(File_iwrite)
|
|
DEFAULT_WRAPPER_W_3W(File_seek)
|
|
DEFAULT_WRAPPER_W_2W(File_get_position)
|
|
DEFAULT_WRAPPER_W_3W(File_get_byte_offset)
|
|
DEFAULT_WRAPPER_W_5W(File_read_shared)
|
|
DEFAULT_WRAPPER_W_5W(File_write_shared)
|
|
DEFAULT_WRAPPER_W_5W(File_iread_shared)
|
|
DEFAULT_WRAPPER_W_5W(File_iwrite_shared)
|
|
DEFAULT_WRAPPER_W_5W(File_read_ordered)
|
|
DEFAULT_WRAPPER_W_5W(File_write_ordered)
|
|
DEFAULT_WRAPPER_W_3W(File_seek_shared)
|
|
DEFAULT_WRAPPER_W_2W(File_get_position_shared)
|
|
DEFAULT_WRAPPER_W_5W(File_read_at_all_begin)
|
|
DEFAULT_WRAPPER_W_3W(File_read_at_all_end)
|
|
DEFAULT_WRAPPER_W_5W(File_write_at_all_begin)
|
|
DEFAULT_WRAPPER_W_3W(File_write_at_all_end)
|
|
DEFAULT_WRAPPER_W_4W(File_read_all_begin)
|
|
DEFAULT_WRAPPER_W_3W(File_read_all_end)
|
|
DEFAULT_WRAPPER_W_4W(File_write_all_begin)
|
|
DEFAULT_WRAPPER_W_3W(File_write_all_end)
|
|
DEFAULT_WRAPPER_W_4W(File_read_ordered_begin)
|
|
DEFAULT_WRAPPER_W_3W(File_read_ordered_end)
|
|
DEFAULT_WRAPPER_W_4W(File_write_ordered_begin)
|
|
DEFAULT_WRAPPER_W_3W(File_write_ordered_end)
|
|
DEFAULT_WRAPPER_W_3W(File_get_type_extent)
|
|
DEFAULT_WRAPPER_W_2W(File_set_atomicity)
|
|
DEFAULT_WRAPPER_W_2W(File_get_atomicity)
|
|
DEFAULT_WRAPPER_W_1W(File_sync)
|
|
/* DEFAULT_WRAPPER_W_0W(Finalize) */
|
|
DEFAULT_WRAPPER_W_1W(Finalized)
|
|
DEFAULT_WRAPPER_W_1W(Free_mem)
|
|
/* DEFAULT_WRAPPER_W_8W(Gather) */
|
|
DEFAULT_WRAPPER_W_9W(Gatherv)
|
|
DEFAULT_WRAPPER_W_2W(Get_address)
|
|
/* DEFAULT_WRAPPER_W_3W(Get_count) */
|
|
DEFAULT_WRAPPER_W_3W(Get_elements)
|
|
DEFAULT_WRAPPER_W_8W(Get)
|
|
DEFAULT_WRAPPER_W_2W(Get_processor_name)
|
|
DEFAULT_WRAPPER_W_2W(Get_version)
|
|
DEFAULT_WRAPPER_W_6W(Graph_create)
|
|
DEFAULT_WRAPPER_W_5W(Graph_get)
|
|
DEFAULT_WRAPPER_W_5W(Graph_map)
|
|
DEFAULT_WRAPPER_W_3W(Graph_neighbors_count)
|
|
DEFAULT_WRAPPER_W_4W(Graph_neighbors)
|
|
DEFAULT_WRAPPER_W_3W(Graphdims_get)
|
|
DEFAULT_WRAPPER_W_1W(Grequest_complete)
|
|
DEFAULT_WRAPPER_W_5W(Grequest_start)
|
|
DEFAULT_WRAPPER_W_1W(Group_c2f)
|
|
DEFAULT_WRAPPER_W_3W(Group_compare)
|
|
DEFAULT_WRAPPER_W_3W(Group_difference)
|
|
DEFAULT_WRAPPER_W_4W(Group_excl)
|
|
DEFAULT_WRAPPER_W_1W(Group_f2c)
|
|
DEFAULT_WRAPPER_W_1W(Group_free)
|
|
DEFAULT_WRAPPER_W_4W(Group_incl)
|
|
DEFAULT_WRAPPER_W_3W(Group_intersection)
|
|
DEFAULT_WRAPPER_W_4W(Group_range_excl)
|
|
DEFAULT_WRAPPER_W_4W(Group_range_incl)
|
|
DEFAULT_WRAPPER_W_2W(Group_rank)
|
|
DEFAULT_WRAPPER_W_2W(Group_size)
|
|
DEFAULT_WRAPPER_W_5W(Group_translate_ranks)
|
|
DEFAULT_WRAPPER_W_3W(Group_union)
|
|
/* DEFAULT_WRAPPER_W_7W(Ibsend) */
|
|
DEFAULT_WRAPPER_W_1W(Info_c2f)
|
|
DEFAULT_WRAPPER_W_1W(Info_create)
|
|
DEFAULT_WRAPPER_W_2W(Info_delete)
|
|
DEFAULT_WRAPPER_W_2W(Info_dup)
|
|
DEFAULT_WRAPPER_W_1W(Info_f2c)
|
|
DEFAULT_WRAPPER_W_1W(Info_free)
|
|
DEFAULT_WRAPPER_W_5W(Info_get)
|
|
DEFAULT_WRAPPER_W_2W(Info_get_nkeys)
|
|
DEFAULT_WRAPPER_W_3W(Info_get_nthkey)
|
|
DEFAULT_WRAPPER_W_4W(Info_get_valuelen)
|
|
DEFAULT_WRAPPER_W_3W(Info_set)
|
|
/* DEFAULT_WRAPPER_W_2W(Init) */
|
|
/* DEFAULT_WRAPPER_W_1W(Initialized) */
|
|
DEFAULT_WRAPPER_W_4W(Init_thread)
|
|
DEFAULT_WRAPPER_W_6W(Intercomm_create)
|
|
DEFAULT_WRAPPER_W_3W(Intercomm_merge)
|
|
/* DEFAULT_WRAPPER_W_5W(Iprobe) */
|
|
/* DEFAULT_WRAPPER_W_7W(Irecv) */
|
|
/* DEFAULT_WRAPPER_W_7W(Irsend) */
|
|
/* DEFAULT_WRAPPER_W_7W(Isend) */
|
|
/* DEFAULT_WRAPPER_W_7W(Issend) */
|
|
DEFAULT_WRAPPER_W_1W(Is_thread_main)
|
|
DEFAULT_WRAPPER_W_4W(Keyval_create)
|
|
DEFAULT_WRAPPER_W_1W(Keyval_free)
|
|
DEFAULT_WRAPPER_W_3W(Lookup_name)
|
|
DEFAULT_WRAPPER_W_1W(Op_c2f)
|
|
/* DEFAULT_WRAPPER_W_3W(Op_create) */
|
|
DEFAULT_WRAPPER_W_2W(Open_port)
|
|
DEFAULT_WRAPPER_W_1W(Op_f2c)
|
|
DEFAULT_WRAPPER_W_1W(Op_free)
|
|
DEFAULT_WRAPPER_W_7W(Pack_external)
|
|
DEFAULT_WRAPPER_W_4W(Pack_external_size)
|
|
/* DEFAULT_WRAPPER_W_7W(Pack) */
|
|
DEFAULT_WRAPPER_W_4W(Pack_size)
|
|
/* int MPI_Pcontrol(const int level, ...) */
|
|
/* DEFAULT_WRAPPER_W_4W(Probe) */
|
|
DEFAULT_WRAPPER_W_3W(Publish_name)
|
|
DEFAULT_WRAPPER_W_8W(Put)
|
|
DEFAULT_WRAPPER_W_1W(Query_thread)
|
|
DEFAULT_WRAPPER_W_7W(Recv_init)
|
|
/* DEFAULT_WRAPPER_W_7W(Recv) */
|
|
/* DEFAULT_WRAPPER_W_7W(Reduce) */
|
|
DEFAULT_WRAPPER_W_6W(Reduce_scatter)
|
|
DEFAULT_WRAPPER_W_5W(Register_datarep)
|
|
DEFAULT_WRAPPER_W_1W(Request_c2f)
|
|
DEFAULT_WRAPPER_W_1W(Request_f2c)
|
|
DEFAULT_WRAPPER_W_1W(Request_free)
|
|
DEFAULT_WRAPPER_W_3W(Request_get_status)
|
|
/* DEFAULT_WRAPPER_W_6W(Rsend) */
|
|
DEFAULT_WRAPPER_W_7W(Rsend_init)
|
|
DEFAULT_WRAPPER_W_6W(Scan)
|
|
/* DEFAULT_WRAPPER_W_8W(Scatter) */
|
|
DEFAULT_WRAPPER_W_9W(Scatterv)
|
|
DEFAULT_WRAPPER_W_7W(Send_init)
|
|
/* DEFAULT_WRAPPER_W_6W(Send) */
|
|
/* DEFAULT_WRAPPER_W_12W(Sendrecv) */
|
|
DEFAULT_WRAPPER_W_9W(Sendrecv_replace)
|
|
DEFAULT_WRAPPER_W_7W(Ssend_init)
|
|
/* DEFAULT_WRAPPER_W_6W(Ssend) */
|
|
DEFAULT_WRAPPER_W_1W(Start)
|
|
DEFAULT_WRAPPER_W_2W(Startall)
|
|
DEFAULT_WRAPPER_W_2W(Status_c2f)
|
|
DEFAULT_WRAPPER_W_2W(Status_f2c)
|
|
DEFAULT_WRAPPER_W_2W(Status_set_cancelled)
|
|
DEFAULT_WRAPPER_W_3W(Status_set_elements)
|
|
/* DEFAULT_WRAPPER_W_4W(Testall) */
|
|
DEFAULT_WRAPPER_W_5W(Testany)
|
|
/* DEFAULT_WRAPPER_W_3W(Test) */
|
|
DEFAULT_WRAPPER_W_2W(Test_cancelled)
|
|
DEFAULT_WRAPPER_W_5W(Testsome)
|
|
DEFAULT_WRAPPER_W_2W(Topo_test)
|
|
DEFAULT_WRAPPER_W_1W(Type_c2f)
|
|
/* DEFAULT_WRAPPER_W_1W(Type_commit) */
|
|
DEFAULT_WRAPPER_W_3W(Type_contiguous)
|
|
DEFAULT_WRAPPER_W_10W(Type_create_darray)
|
|
DEFAULT_WRAPPER_W_3W(Type_create_f90_complex)
|
|
DEFAULT_WRAPPER_W_2W(Type_create_f90_integer)
|
|
DEFAULT_WRAPPER_W_3W(Type_create_f90_real)
|
|
DEFAULT_WRAPPER_W_5W(Type_create_hindexed)
|
|
DEFAULT_WRAPPER_W_5W(Type_create_hvector)
|
|
DEFAULT_WRAPPER_W_4W(Type_create_keyval)
|
|
DEFAULT_WRAPPER_W_5W(Type_create_indexed_block)
|
|
DEFAULT_WRAPPER_W_5W(Type_create_struct)
|
|
DEFAULT_WRAPPER_W_7W(Type_create_subarray)
|
|
DEFAULT_WRAPPER_W_4W(Type_create_resized)
|
|
DEFAULT_WRAPPER_W_2W(Type_delete_attr)
|
|
DEFAULT_WRAPPER_W_2W(Type_dup)
|
|
/* DEFAULT_WRAPPER_W_2W(Type_extent) */
|
|
/* DEFAULT_WRAPPER_W_1W(Type_free) */
|
|
DEFAULT_WRAPPER_W_1W(Type_free_keyval)
|
|
DEFAULT_WRAPPER_W_1W(Type_f2c)
|
|
DEFAULT_WRAPPER_W_4W(Type_get_attr)
|
|
/* DEFAULT_WRAPPER_W_7W(Type_get_contents) */
|
|
/* DEFAULT_WRAPPER_W_5W(Type_get_envelope) */
|
|
DEFAULT_WRAPPER_W_3W(Type_get_extent)
|
|
DEFAULT_WRAPPER_W_3W(Type_get_name)
|
|
DEFAULT_WRAPPER_W_3W(Type_get_true_extent)
|
|
DEFAULT_WRAPPER_W_5W(Type_hindexed)
|
|
DEFAULT_WRAPPER_W_5W(Type_hvector)
|
|
DEFAULT_WRAPPER_W_5W(Type_indexed)
|
|
DEFAULT_WRAPPER_W_2W(Type_lb)
|
|
DEFAULT_WRAPPER_W_3W(Type_match_size)
|
|
DEFAULT_WRAPPER_W_3W(Type_set_attr)
|
|
DEFAULT_WRAPPER_W_2W(Type_set_name)
|
|
DEFAULT_WRAPPER_W_2W(Type_size)
|
|
DEFAULT_WRAPPER_W_5W(Type_struct)
|
|
DEFAULT_WRAPPER_W_2W(Type_ub)
|
|
DEFAULT_WRAPPER_W_5W(Type_vector)
|
|
/* DEFAULT_WRAPPER_W_7W(Unpack) */
|
|
DEFAULT_WRAPPER_W_3W(Unpublish_name)
|
|
DEFAULT_WRAPPER_W_7W(Unpack_external)
|
|
/* DEFAULT_WRAPPER_W_3W(Waitall) */
|
|
/* DEFAULT_WRAPPER_W_4W(Waitany) */
|
|
/* DEFAULT_WRAPPER_W_2W(Wait) */
|
|
DEFAULT_WRAPPER_W_5W(Waitsome)
|
|
DEFAULT_WRAPPER_W_1W(Win_c2f)
|
|
DEFAULT_WRAPPER_W_2W(Win_call_errhandler)
|
|
DEFAULT_WRAPPER_W_1W(Win_complete)
|
|
DEFAULT_WRAPPER_W_6W(Win_create)
|
|
DEFAULT_WRAPPER_W_2W(Win_create_errhandler)
|
|
DEFAULT_WRAPPER_W_4W(Win_create_keyval)
|
|
DEFAULT_WRAPPER_W_2W(Win_delete_attr)
|
|
DEFAULT_WRAPPER_W_1W(Win_f2c)
|
|
DEFAULT_WRAPPER_W_2W(Win_fence)
|
|
DEFAULT_WRAPPER_W_1W(Win_free)
|
|
DEFAULT_WRAPPER_W_1W(Win_free_keyval)
|
|
DEFAULT_WRAPPER_W_4W(Win_get_attr)
|
|
DEFAULT_WRAPPER_W_2W(Win_get_errhandler)
|
|
DEFAULT_WRAPPER_W_2W(Win_get_group)
|
|
DEFAULT_WRAPPER_W_3W(Win_get_name)
|
|
DEFAULT_WRAPPER_W_4W(Win_lock)
|
|
DEFAULT_WRAPPER_W_3W(Win_post)
|
|
DEFAULT_WRAPPER_W_3W(Win_set_attr)
|
|
DEFAULT_WRAPPER_W_2W(Win_set_errhandler)
|
|
DEFAULT_WRAPPER_W_2W(Win_set_name)
|
|
DEFAULT_WRAPPER_W_3W(Win_start)
|
|
DEFAULT_WRAPPER_W_2W(Win_test)
|
|
DEFAULT_WRAPPER_W_2W(Win_unlock)
|
|
DEFAULT_WRAPPER_W_1W(Win_wait)
|
|
/* double MPI_Wtick(void) */
|
|
/* double MPI_Wtime(void) */
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- ---*/
|
|
/*--- ---*/
|
|
/*--- ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------*/
|
|
/*--- end mpiwrap.c ---*/
|
|
/*---------------------------------------------------------------*/
|