From d7fc07163e3f29ca19227344fb03ce5d4be36e7d Mon Sep 17 00:00:00 2001 From: Shane Snyder Date: Wed, 25 Apr 2018 17:21:48 -0500 Subject: [PATCH 01/80] expose generic aiori_ calls for access, etc. --- src/aiori.c | 36 ++++++++++++++++++++++++++---------- src/aiori.h | 7 +++++++ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/aiori.c b/src/aiori.c index 95d3a07..ccabcb8 100644 --- a/src/aiori.c +++ b/src/aiori.c @@ -61,7 +61,7 @@ ior_aiori_t *available_aiori[] = { * This function provides a AIORI statfs for POSIX-compliant filesystems. It * uses statvfs is available and falls back on statfs. */ -static int aiori_statfs (const char *path, ior_aiori_statfs_t *stat_buf, IOR_param_t * param) +int aiori_posix_statfs (const char *path, ior_aiori_statfs_t *stat_buf, IOR_param_t * param) { int ret; #if defined(HAVE_STATVFS) @@ -86,44 +86,60 @@ static int aiori_statfs (const char *path, ior_aiori_statfs_t *stat_buf, IOR_par return 0; } -static int aiori_mkdir (const char *path, mode_t mode, IOR_param_t * param) +int aiori_posix_mkdir (const char *path, mode_t mode, IOR_param_t * param) { return mkdir (path, mode); } -static int aiori_rmdir (const char *path, IOR_param_t * param) +int aiori_posix_rmdir (const char *path, IOR_param_t * param) { return rmdir (path); } -static int aiori_access (const char *path, int mode, IOR_param_t * param) +int aiori_posix_access (const char *path, int mode, IOR_param_t * param) { return access (path, mode); } -static int aiori_stat (const char *path, struct stat *buf, IOR_param_t * param) +int aiori_posix_stat (const char *path, struct stat *buf, IOR_param_t * param) { return stat (path, buf); } const ior_aiori_t *aiori_select (const char *api) { + char warn_str[256] = {0}; for (ior_aiori_t **tmp = available_aiori ; *tmp != NULL; ++tmp) { if (NULL == api || strcasecmp(api, (*tmp)->name) == 0) { if (NULL == (*tmp)->statfs) { - (*tmp)->statfs = aiori_statfs; + (*tmp)->statfs = aiori_posix_statfs; + snprintf(warn_str, 256, "assuming POSIX-based backend for" + " %s statfs call", api); + WARN(warn_str); } if (NULL == (*tmp)->mkdir) { - (*tmp)->mkdir = aiori_mkdir; + (*tmp)->mkdir = aiori_posix_mkdir; + snprintf(warn_str, 256, "assuming POSIX-based backend for" + " %s mkdir call", api); + WARN(warn_str); } if (NULL == (*tmp)->rmdir) { - (*tmp)->rmdir = aiori_rmdir; + (*tmp)->rmdir = aiori_posix_rmdir; + snprintf(warn_str, 256, "assuming POSIX-based backend for" + " %s rmdir call", api); + WARN(warn_str); } if (NULL == (*tmp)->access) { - (*tmp)->access = aiori_access; + (*tmp)->access = aiori_posix_access; + snprintf(warn_str, 256, "assuming POSIX-based backend for" + " %s access call", api); + WARN(warn_str); } if (NULL == (*tmp)->stat) { - (*tmp)->stat = aiori_stat; + (*tmp)->stat = aiori_posix_stat; + snprintf(warn_str, 256, "assuming POSIX-based backend for" + " %s stat call", api); + WARN(warn_str); } return *tmp; } diff --git a/src/aiori.h b/src/aiori.h index 06f733c..49b8c62 100755 --- a/src/aiori.h +++ b/src/aiori.h @@ -92,6 +92,13 @@ const ior_aiori_t *aiori_select (const char *api); int aiori_count (void); const char *aiori_default (void); +/* some generic POSIX-based backend calls */ +int aiori_posix_statfs (const char *path, ior_aiori_statfs_t *stat_buf, IOR_param_t * param); +int aiori_posix_mkdir (const char *path, mode_t mode, IOR_param_t * param); +int aiori_posix_rmdir (const char *path, IOR_param_t * param); +int aiori_posix_access (const char *path, int mode, IOR_param_t * param); +int aiori_posix_stat (const char *path, struct stat *buf, IOR_param_t * param); + IOR_offset_t MPIIO_GetFileSize(IOR_param_t * test, MPI_Comm testComm, char *testFileName); From 60243e02778a95bab65ef1ab318ee2ed3f096882 Mon Sep 17 00:00:00 2001 From: Shane Snyder Date: Wed, 25 Apr 2018 17:32:16 -0500 Subject: [PATCH 02/80] expose MPIIO access and delete calls --- src/aiori-MPIIO.c | 52 +++++++++++++++++++++++------------------------ src/aiori.h | 3 +++ 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/aiori-MPIIO.c b/src/aiori-MPIIO.c index 2fec74f..ac766ff 100755 --- a/src/aiori-MPIIO.c +++ b/src/aiori-MPIIO.c @@ -38,10 +38,8 @@ static void *MPIIO_Open(char *, IOR_param_t *); static IOR_offset_t MPIIO_Xfer(int, void *, IOR_size_t *, IOR_offset_t, IOR_param_t *); static void MPIIO_Close(void *, IOR_param_t *); -static void MPIIO_Delete(char *, IOR_param_t *); static void MPIIO_SetVersion(IOR_param_t *); static void MPIIO_Fsync(void *, IOR_param_t *); -static int MPIIO_Access(const char *, int, IOR_param_t *); /************************** D E C L A R A T I O N S ***************************/ @@ -60,30 +58,6 @@ ior_aiori_t mpiio_aiori = { /***************************** F U N C T I O N S ******************************/ -/* - * Try to access a file through the MPIIO interface. - */ -static int MPIIO_Access(const char *path, int mode, IOR_param_t *param) -{ - MPI_File fd; - int mpi_mode = MPI_MODE_UNIQUE_OPEN; - - if ((mode & W_OK) && (mode & R_OK)) - mpi_mode |= MPI_MODE_RDWR; - else if (mode & W_OK) - mpi_mode |= MPI_MODE_WRONLY; - else - mpi_mode |= MPI_MODE_RDONLY; - - int ret = MPI_File_open(MPI_COMM_SELF, path, mpi_mode, - MPI_INFO_NULL, &fd); - - if (!ret) - MPI_File_close(&fd); - - return ret; -} - /* * Create and open a file through the MPIIO interface. */ @@ -396,7 +370,7 @@ static void MPIIO_Close(void *fd, IOR_param_t * param) /* * Delete a file through the MPIIO interface. */ -static void MPIIO_Delete(char *testFileName, IOR_param_t * param) +void MPIIO_Delete(char *testFileName, IOR_param_t * param) { MPI_CHECK(MPI_File_delete(testFileName, (MPI_Info) MPI_INFO_NULL), "cannot delete file"); @@ -495,3 +469,27 @@ IOR_offset_t MPIIO_GetFileSize(IOR_param_t * test, MPI_Comm testComm, return (aggFileSizeFromStat); } + +/* + * Try to access a file through the MPIIO interface. + */ +int MPIIO_Access(const char *path, int mode, IOR_param_t *param) +{ + MPI_File fd; + int mpi_mode = MPI_MODE_UNIQUE_OPEN; + + if ((mode & W_OK) && (mode & R_OK)) + mpi_mode |= MPI_MODE_RDWR; + else if (mode & W_OK) + mpi_mode |= MPI_MODE_WRONLY; + else + mpi_mode |= MPI_MODE_RDONLY; + + int ret = MPI_File_open(MPI_COMM_SELF, path, mpi_mode, + MPI_INFO_NULL, &fd); + + if (!ret) + MPI_File_close(&fd); + + return ret; +} diff --git a/src/aiori.h b/src/aiori.h index 49b8c62..a54bd59 100755 --- a/src/aiori.h +++ b/src/aiori.h @@ -99,7 +99,10 @@ int aiori_posix_rmdir (const char *path, IOR_param_t * param); int aiori_posix_access (const char *path, int mode, IOR_param_t * param); int aiori_posix_stat (const char *path, struct stat *buf, IOR_param_t * param); +/* NOTE: these 3 MPI-IO functions are exported for reuse by HDF5/PNetCDF */ +void MPIIO_Delete(char *testFileName, IOR_param_t * param); IOR_offset_t MPIIO_GetFileSize(IOR_param_t * test, MPI_Comm testComm, char *testFileName); +int MPIIO_Access(const char *, int, IOR_param_t *); #endif /* not _AIORI_H */ From 7233bee95364e021c3528f80ba38e9ef1a7f418c Mon Sep 17 00:00:00 2001 From: Shane Snyder Date: Wed, 25 Apr 2018 17:34:25 -0500 Subject: [PATCH 03/80] make ncmpi/hdf5 use mpiio access/delete --- src/aiori-HDF5.c | 19 ++++++++++++++++--- src/aiori-MPIIO.c | 4 ++++ src/aiori-NCMPI.c | 19 ++++++++++++++++--- src/aiori-POSIX.c | 5 +++++ 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/aiori-HDF5.c b/src/aiori-HDF5.c index e957605..5d629e1 100755 --- a/src/aiori-HDF5.c +++ b/src/aiori-HDF5.c @@ -92,6 +92,7 @@ static void HDF5_Delete(char *, IOR_param_t *); static void HDF5_SetVersion(IOR_param_t *); static void HDF5_Fsync(void *, IOR_param_t *); static IOR_offset_t HDF5_GetFileSize(IOR_param_t *, MPI_Comm, char *); +static int HDF5_Access(const char *, int, IOR_param_t *); /************************** D E C L A R A T I O N S ***************************/ @@ -105,6 +106,11 @@ ior_aiori_t hdf5_aiori = { .set_version = HDF5_SetVersion, .fsync = HDF5_Fsync, .get_file_size = HDF5_GetFileSize, + .statfs = aiori_posix_statfs, + .mkdir = aiori_posix_mkdir, + .rmdir = aiori_posix_rmdir, + .access = HDF5_Access, + .stat = aiori_posix_stat, }; static hid_t xferPropList; /* xfer property list */ @@ -435,8 +441,7 @@ static void HDF5_Close(void *fd, IOR_param_t * param) */ static void HDF5_Delete(char *testFileName, IOR_param_t * param) { - if (unlink(testFileName) != 0) - WARN("cannot delete file"); + return(MPIIO_Delete(testFileName, param)); } /* @@ -565,5 +570,13 @@ static void SetupDataSet(void *fd, IOR_param_t * param) static IOR_offset_t HDF5_GetFileSize(IOR_param_t * test, MPI_Comm testComm, char *testFileName) { - return (MPIIO_GetFileSize(test, testComm, testFileName)); + return(MPIIO_GetFileSize(test, testComm, testFileName)); +} + +/* + * Use MPIIO call to check for access. + */ +static int HDF5_Access(const char *path, int mode, IOR_param_t *param) +{ + return(MPIIO_Access(path, mode, param)); } diff --git a/src/aiori-MPIIO.c b/src/aiori-MPIIO.c index ac766ff..8f04310 100755 --- a/src/aiori-MPIIO.c +++ b/src/aiori-MPIIO.c @@ -53,7 +53,11 @@ ior_aiori_t mpiio_aiori = { .set_version = MPIIO_SetVersion, .fsync = MPIIO_Fsync, .get_file_size = MPIIO_GetFileSize, + .statfs = aiori_posix_statfs, + .mkdir = aiori_posix_mkdir, + .rmdir = aiori_posix_rmdir, .access = MPIIO_Access, + .stat = aiori_posix_stat, }; /***************************** F U N C T I O N S ******************************/ diff --git a/src/aiori-NCMPI.c b/src/aiori-NCMPI.c index 98fd6bd..d990dfd 100755 --- a/src/aiori-NCMPI.c +++ b/src/aiori-NCMPI.c @@ -56,6 +56,7 @@ static void NCMPI_Delete(char *, IOR_param_t *); static void NCMPI_SetVersion(IOR_param_t *); static void NCMPI_Fsync(void *, IOR_param_t *); static IOR_offset_t NCMPI_GetFileSize(IOR_param_t *, MPI_Comm, char *); +static int NCMPI_Access(const char *, int, IOR_param_t *); /************************** D E C L A R A T I O N S ***************************/ @@ -69,6 +70,11 @@ ior_aiori_t ncmpi_aiori = { .set_version = NCMPI_SetVersion, .fsync = NCMPI_Fsync, .get_file_size = NCMPI_GetFileSize, + .statfs = aiori_posix_statfs, + .mkdir = aiori_posix_mkdir, + .rmdir = aiori_posix_rmdir, + .access = NCMPI_Access, + .stat = aiori_posix_stat, }; /***************************** F U N C T I O N S ******************************/ @@ -329,8 +335,7 @@ static void NCMPI_Close(void *fd, IOR_param_t * param) */ static void NCMPI_Delete(char *testFileName, IOR_param_t * param) { - if (unlink(testFileName) != 0) - WARN("unlink() failed"); + return(MPIIO_Delete(testFileName, param)); } /* @@ -387,5 +392,13 @@ static int GetFileMode(IOR_param_t * param) static IOR_offset_t NCMPI_GetFileSize(IOR_param_t * test, MPI_Comm testComm, char *testFileName) { - return (MPIIO_GetFileSize(test, testComm, testFileName)); + return(MPIIO_GetFileSize(test, testComm, testFileName)); +} + +/* + * Use MPIIO call to check for access. + */ +static int NCMPI_Access(const char *path, int mode, IOR_param_t *param) +{ + return(MPIIO_Access(path, mode, param)); } diff --git a/src/aiori-POSIX.c b/src/aiori-POSIX.c index 969f7a1..d562ee2 100755 --- a/src/aiori-POSIX.c +++ b/src/aiori-POSIX.c @@ -87,6 +87,11 @@ ior_aiori_t posix_aiori = { .set_version = POSIX_SetVersion, .fsync = POSIX_Fsync, .get_file_size = POSIX_GetFileSize, + .statfs = aiori_posix_statfs, + .mkdir = aiori_posix_mkdir, + .rmdir = aiori_posix_rmdir, + .access = aiori_posix_access, + .stat = aiori_posix_stat, }; /***************************** F U N C T I O N S ******************************/ From 9b75f071f47c969562d7455375b546b33146b36d Mon Sep 17 00:00:00 2001 From: Shane Snyder Date: Wed, 25 Apr 2018 17:35:10 -0500 Subject: [PATCH 04/80] use backend->access in IOR source --- src/ior.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/ior.c b/src/ior.c index a6029cd..c025e95 100755 --- a/src/ior.c +++ b/src/ior.c @@ -1272,7 +1272,7 @@ static void RemoveFile(char *testFileName, int filePerProc, IOR_param_t * test) rankOffset = 0; GetTestFileName(testFileName, test); } - if (access(testFileName, F_OK) == 0) { + if (backend->access(testFileName, F_OK, test) == 0) { backend->delete(testFileName, test); } if (test->reorderTasksRandom == TRUE) { @@ -1280,13 +1280,7 @@ static void RemoveFile(char *testFileName, int filePerProc, IOR_param_t * test) GetTestFileName(testFileName, test); } } else { - // BUG: "access()" assumes a POSIX filesystem. Maybe use - // backend->get_file_size(), instead, (and catch - // errors), or extend the aiori struct to include - // something to safely check for existence of the - // "file". - // - if ((rank == 0) && (access(testFileName, F_OK) == 0)) { + if ((rank == 0) && (backend->access(testFileName, F_OK, test) == 0)) { backend->delete(testFileName, test); } } From 76f3d341b99a82663779d4c290a79af1d6001fa8 Mon Sep 17 00:00:00 2001 From: Shane Snyder Date: Fri, 27 Apr 2018 11:33:52 -0500 Subject: [PATCH 05/80] implement MPIIO sync --- src/aiori-MPIIO.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/aiori-MPIIO.c b/src/aiori-MPIIO.c index 8f04310..af8094b 100755 --- a/src/aiori-MPIIO.c +++ b/src/aiori-MPIIO.c @@ -342,6 +342,8 @@ static IOR_offset_t MPIIO_Xfer(int access, void *fd, IOR_size_t * buffer, } } } + if((access == WRITE) && (param->fsyncPerWrite == TRUE)) + MPIIO_Fsync(fd, param); return (length); } @@ -350,7 +352,8 @@ static IOR_offset_t MPIIO_Xfer(int access, void *fd, IOR_size_t * buffer, */ static void MPIIO_Fsync(void *fd, IOR_param_t * param) { - ; + if (MPI_File_sync(*(MPI_File *)fd) != MPI_SUCCESS) + EWARN("fsync() failed"); } /* From 92dfb67e5cbe9ee7ef54b77a6a7cab5e2270a6a9 Mon Sep 17 00:00:00 2001 From: Shane Snyder Date: Fri, 27 Apr 2018 12:44:20 -0500 Subject: [PATCH 06/80] allow fsync in POSIX, MPIIO, and HDFS --- src/ior.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ior.c b/src/ior.c index c025e95..3d8c3dc 100755 --- a/src/ior.c +++ b/src/ior.c @@ -2371,8 +2371,10 @@ static void ValidateTests(IOR_param_t * test) if ((strcasecmp(test->api, "POSIX") != 0) && test->singleXferAttempt) WARN_RESET("retry only available in POSIX", test, &defaults, singleXferAttempt); - if ((strcasecmp(test->api, "POSIX") != 0) && test->fsync) - WARN_RESET("fsync() only available in POSIX", + if (((strcasecmp(test->api, "POSIX") != 0) + && (strcasecmp(test->api, "MPIIO") != 0) + && (strcasecmp(test->api, "HDFS") != 0)) && test->fsync) + WARN_RESET("fsync() not supported in selected backend", test, &defaults, fsync); if ((strcasecmp(test->api, "MPIIO") != 0) && test->preallocate) WARN_RESET("preallocation only available in MPIIO", From a3c773d006c94286432cd8c8abf4cb3ec74e995d Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Fri, 6 Jul 2018 21:47:16 +0100 Subject: [PATCH 07/80] Cleanup of old testing code. --- scripts/exampleScript | 24 - scripts/run_script.cnl | 93 - testing/IOR-tester.README | 46 - testing/IOR-tester.py | 1605 ----------------- .../basic-tests.sh | 0 testing/hintsFile | 2 - testing/timestamp.cfg | 28 - 7 files changed, 1798 deletions(-) delete mode 100755 scripts/exampleScript delete mode 100755 scripts/run_script.cnl delete mode 100755 testing/IOR-tester.README delete mode 100755 testing/IOR-tester.py rename scripts/run_script.linux => testing/basic-tests.sh (100%) delete mode 100755 testing/hintsFile delete mode 100644 testing/timestamp.cfg diff --git a/scripts/exampleScript b/scripts/exampleScript deleted file mode 100755 index c17e284..0000000 --- a/scripts/exampleScript +++ /dev/null @@ -1,24 +0,0 @@ -IOR START - testFile = /tmp/work/swh13/testfile - filePerProc=1 - api=POSIX - repetitions=2 - verbose=1 - reordertasksrandom=1 - reordertasksrandomseed=-113 - RUN - verbose = 2 - repetitions=1#more foo - reordertasksconstant=1 - #foobar - blockSize=10m - transferSize=128k - randomoffset=1 - - RUN - verbose = 0 - - #blockSize= - transferSize=64k - RUN -IOR STOP diff --git a/scripts/run_script.cnl b/scripts/run_script.cnl deleted file mode 100755 index a38edbd..0000000 --- a/scripts/run_script.cnl +++ /dev/null @@ -1,93 +0,0 @@ -#!/bin/bash -x - -#PBS -N IOR -#PBS -j oe -#PBS -q batch -#PBS -A stf006 -#PBS -V -#PBS -l walltime=0:60:00,size=8 - -VERS=IOR-2.10.1.ornl.16 -WORK=/tmp/work/${USER} -echo $PBS_O_WORKDIR - -cd /ccs/proj/quadcore -tar -czvf ${WORK}/${VERS}.tar.gz ./${VERS} -cd ${WORK} -rm -fr ./${VERS} -tar -xzvf ${WORK}/${VERS}.tar.gz -cd ${WORK}/${VERS} -gmake clean -gmake mpiio -EXEC=${WORK}/${VERS}/src/C/IOR -IODIR=/tmp/work/swh13/test_files_x -cd ${WORK}/${VERS}/tests - -which mpirun - -rm -fr $IODIR -mkdir $IODIR - -let "w=128" -let "s=1024*1024" -let "i=3" - -MPIRUN="aprun -n" - -RESULTS="." - -let "tid=1" -XFERS="1048576 262144 32768 4096 1024" -XFERS="262144" -for xfer in `echo $XFERS` -do - let "n=8" -until [ "$n" -gt 8 ] -do - - let "m=$n/4" - #TESTS="POSIX MPIIO HDF5 NCMPI" - TESTS="POSIX MPIIO" - for test in `echo $TESTS` - do - runid="p$n.$xfer.${test}" - date - - V=" " - BLOCKS="1 10 1 10 1 10" - for blocks in `echo $BLOCKS` - do - - let "block=${xfer} * ${blocks}" - - #fileperproc tests - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -w -z ${V} -F -o $IODIR/testwrite.${runid} -Y -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -w -z ${V} -F -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z ${V} -F -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z -C ${V} -F -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z -C -Q $m ${V} -F -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z -Z -Q $m ${V} -F -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z -Z -Q $m -X 13 ${V} -F -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z -Z -Q $m -X -13 ${V} -F -o $IODIR/testwrite.${runid} -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - - #shared tests - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -w -z ${V} -o $IODIR/testwrite.${runid} -Y -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -w ${V} -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z ${V} -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - - #test mutually exclusive options - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z -C ${V} -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z -Z ${V} -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -Z -C ${V} -o $IODIR/testwrite.${runid} -i${i} -m -t ${xfer} -b ${block} -d 0.0 - let "tid=$tid + 17" - - V=$V" -v" - - done #blocks - - date - done #test - let "n = $n * 2" - done #n -done #xfer -exit diff --git a/testing/IOR-tester.README b/testing/IOR-tester.README deleted file mode 100755 index da57cf4..0000000 --- a/testing/IOR-tester.README +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************\ -* * -* Copyright (c) 2003, The Regents of the University of California * -* See the file COPYRIGHT for a complete copyright notice and license. * -* * -\******************************************************************************/ - -The IOR-tester runs a series of tests to check and maintain the existing -functionality of the source code as code is modified. The IOR-tester creates -a default test, then modifies it to run test scripts. It runs a large number -of tests, most which are expected to pass, but some with an expectation of -failure. - -To run the code, modify the 'DefaultTest' dictionary in the source code to -reflect the test file location, the executable location, etc. Then, run -the code using './IOR-tester.py'. - -The expected-pass, pattern-independent tests include: - POSIX only: - o retry transfer - - MPIIO only: - o hints - o preallocation - - Both POSIX and MPIIO: - o repetition count - o intertest delay - o test file removal - o verbosity - -The expected-pass, pattern-dependent tests include: - POSIX: - o write-only, read-only, write/read, and write/read check - o fpp and single file - o segmented, strided - o zero-length, 4-byte, and larger file, block, and transfer sizes - MPIIO (same as POSIX, but using MPIIO access): - o noncollective - o noncollective, file view - o collective - o collective, file view - -The expected-fail tests include: - Both POSIX and MPIIO: - o repetition count diff --git a/testing/IOR-tester.py b/testing/IOR-tester.py deleted file mode 100755 index 499b79c..0000000 --- a/testing/IOR-tester.py +++ /dev/null @@ -1,1605 +0,0 @@ -#!/usr/bin/env python -# -# Tester for IOR -# -#/*****************************************************************************\ -#* * -#* Copyright (c) 2003, The Regents of the University of California * -#* See the file COPYRIGHT for a complete copyright notice and license. * -#* * -#\*****************************************************************************/ - -import sys -import os.path -import string - -# definitions -RETURN_TOTAL_TESTS = -1 -TEST_SETTINGS = 0 -TEST_SUBSET_SIZE = 5 # number of tests in each job submission -IOR_SIZE_T = 8 -KIBIBYTE = 1024 -MEBIBYTE = KIBIBYTE * KIBIBYTE -GIBIBYTE = KIBIBYTE * MEBIBYTE -PASS = 1 -FAIL = 0 -TRUE = 1 -FALSE = 0 -scriptFileBase = './scriptFile' -executable = '../src/C/IOR' -#testDir = './tmp_test_dir' -testDir = '/p/glocal1/loewe/tmp_test_dir' - -debug = FALSE # debug mode = {FALSE, TRUE} - -################################################################################ -# class for default test parameters # -################################################################################ -class Test: - ###################### - # default parameters # - ###################### - def DefaultTest(self): - return { - # general - 'debug': 'debug info', - 'api': 'POSIX', - 'testFile': testDir + '/testFile.1', - 'hintsFileName': '', - 'repetitions': 1, - 'multiFile': 0, - 'interTestDelay': 0, - 'numTasks': 0, - 'readFile': 1, - 'writeFile': 1, - 'filePerProc': 0, - 'fsync': 0, - 'checkWrite': 1, - 'checkRead': 1, - 'keepFile': 1, - 'keepFileWithError': 1, - 'segmentCount': 1, - 'blockSize': MEBIBYTE, - 'transferSize': (MEBIBYTE / 4), - 'verbose': 0, - 'showHelp': 0, - 'reorderTasks': 1, # not default in code - 'quitOnError': 0, - 'useExistingTestFile': 0, - 'deadlineForStonewalling': 0, - 'maxTimeDuration': 0, - 'setTimeStampSignature': 0, - 'intraTestBarriers': 0, - 'storeFileOffset': 0, - 'randomOffset': 0, - # POSIX - 'singleXferAttempt': 0, - 'useO_DIRECT': 0, - # MPIIO - 'useFileView': 0, - 'preallocate': 0, - 'useSharedFilePointer': 0, # not working yet - 'useStridedDatatype': 0, # not working yet - # non-POSIX - 'showHints': 0, - 'collective': 0, - # HDF5 - 'setAlignment': 1, - 'noFill': 0, # in hdf5-1.6 or later version - 'individualDataSets': 0 # not working yet - } - - ################### - # tests to be run # - ################### - def Tests(self, expectation, testNumber): - -################################################################################ -################################################################################ -# # -# S T A R T O F T E S T S # -# # -################################################################################ -################################################################################ - POSIX_TESTS = [ - # - # pattern independent tests - # - - # POSIX, interTestDelay - [{'debug': 'POSIX interTestDelay', - 'interTestDelay': 0}], - [{'debug': 'POSIX interTestDelay', - 'interTestDelay': 1}], - - # POSIX, intraTestBarriers - [{'debug': 'POSIX intraTestBarriers', - 'intraTestBarriers': 1}], - - # POSIX, uniqueDir - [{'debug': 'POSIX uniqueDir', - 'filePerProc': 1, - 'uniqueDir': 1}], - - # POSIX, uniqueDir random - [{'debug': 'POSIX uniqueDir random', - 'filePerProc': 1, - 'randomOffset': 1, - 'checkRead': 0, - 'uniqueDir': 1}], - - # POSIX, fsync - [{'debug': 'POSIX fsync', - 'fsync': 1}], - - # POSIX, repetitions - [{'debug': 'POSIX repetitions', - 'repetitions': 1}], - [{'debug': 'POSIX repetitions', - 'repetitions': 3}], - - # POSIX, repetitions random - [{'debug': 'POSIX repetitions random', - 'randomOffset': 1, - 'checkRead': 0, - 'repetitions': 3}], - - # POSIX, multiFile - [{'debug': 'POSIX multiFile', - 'repetitions': 3, - 'multiFile': 1}], - - # POSIX, multiFile random - [{'debug': 'POSIX multiFile', - 'repetitions': 3, - 'randomOffset': 1, - 'checkRead': 0, - 'multiFile': 1}], - - # POSIX, writeFile-only - [{'debug': 'POSIX writeFile-only', - 'writeFile': 1, - 'readFile': 0, - 'checkRead': 0}], - - # POSIX, writeFile-only random - [{'debug': 'POSIX writeFile-only random', - 'writeFile': 1, - 'randomOffset': 1, - 'readFile': 0, - 'checkRead': 0}], - - # POSIX, readFile-only - [{'debug': 'POSIX readFile-only', - 'writeFile': 0, - 'checkWrite': 0, - 'readFile': 1}], - - # POSIX, readFile-only - [{'debug': 'POSIX readFile-only random', - 'writeFile': 0, - 'checkWrite': 0, - 'randomOffset': 1, - 'checkRead': 0, - 'readFile': 1}], - - # POSIX, write/read check off - [{'debug': 'POSIX write-read check off', - 'checkWrite': 0, - 'checkRead': 0}], - - # POSIX, write/read check off random - [{'debug': 'POSIX write-read check off random', - 'checkWrite': 0, - 'randomOffset': 1, - 'checkRead': 0}], - - # POSIX, store file offset - [{'debug': 'POSIX store file offset', - 'storeFileOffset':1}], - - # POSIX, remove file - [{'debug': 'POSIX remove file', - 'keepFile': 0}], - - # POSIX, remove file random - [{'debug': 'POSIX remove file', - 'randomOffset': 1, - 'checkRead': 0, - 'keepFile': 0}], - - # POSIX, remove file with error - [{'debug': 'POSIX remove file with error', - 'keepFileWithError': 0}], - - # POSIX, remove file with error random - [{'debug': 'POSIX remove file with error random', - 'randomOffset': 1, - 'checkRead': 0, - 'keepFileWithError': 0}], - - - # POSIX, deadline for stonewalling - [{'debug': 'POSIX deadlineForStonewalling', - 'testFile': test.DefaultTest()['testFile'] + '.stonewall', - 'blockSize': GIBIBYTE, - 'readFile': 0, - 'checkWrite': 0, - 'checkRead': 0, - 'deadlineForStonewalling':1}], - - # POSIX, deadline for stonewalling random - [{'debug': 'POSIX deadlineForStonewalling random', - 'testFile': test.DefaultTest()['testFile'] + '.stonewall', - 'blockSize': GIBIBYTE, - 'readFile': 0, - 'checkWrite': 0, - 'randomOffset': 1, - 'checkRead': 0, - 'deadlineForStonewalling':1}], - - # POSIX, max time duration - [{'debug': 'POSIX maxTimeDuration', - 'maxTimeDuration':1}], - - # POSIX, max time duration random - [{'debug': 'POSIX maxTimeDuration random', - 'randomOffset': 1, - 'checkRead': 0, - 'maxTimeDuration':1}], - - # POSIX, verbose - [{'debug': 'POSIX verbose 0', - 'verbose': 0}], - [{'debug': 'POSIX verbose 1', - 'verbose': 1}], - [{'debug': 'POSIX verbose 2', - 'verbose': 2}], - [{'debug': 'POSIX verbose 3', - 'verbose': 3}], - [{'debug': 'POSIX verbose 4', - 'verbose': 4, - 'blockSize': KIBIBYTE, - 'transferSize': (KIBIBYTE / 4)}], - - - # POSIX, multiple file names, ssf - [{'debug': 'POSIX multiple file names ssf', - 'testFile': testDir + '/f1@' + testDir + '/f2'}], - - # POSIX, multiple file names, ssf random - [{'debug': 'POSIX multiple file names ssf random', - 'randomOffset': 1, - 'checkRead': 0, - 'testFile': testDir + '/f1@' + testDir + '/f2'}], - - # POSIX, multiple file names, fpp - [{'debug': 'POSIX multiple file names fpp', - 'filePerProc': 1, - 'testFile': testDir + '/f1@' + testDir + '/f2'}], - - # POSIX, multiple file names, fpp random - [{'debug': 'POSIX multiple file names fpp random', - 'filePerProc': 1, - 'randomOffset': 1, - 'checkRead': 0, - 'testFile': testDir + '/f1@' + testDir + '/f2'}], - - # POSIX, corruptFile - [{'debug': 'POSIX corruptFile', - 'testFile': test.DefaultTest()['testFile'], - 'filePerProc': 0, - 'corruptFile': 1}], - - # POSIX, corruptFile random - [{'debug': 'POSIX corruptFile random', - 'testFile': test.DefaultTest()['testFile'], - 'filePerProc': 0, - 'randomOffset': 1, - 'checkRead': 0, - 'corruptFile': 1}], - - # POSIX, corruptFile - [{'debug': 'POSIX corruptFile filePerProc', - 'filePerProc': 1, - 'corruptFile': 1}], - - # POSIX, corruptFile random - [{'debug': 'POSIX corruptFile filePerProc random', - 'filePerProc': 1, - 'randomOffset': 1, - 'checkRead': 0, - 'corruptFile': 1}], - - # POSIX, showHelp - [{'debug': 'POSIX showHelp', - 'showHelp': 1, - 'filePerProc': 0, - 'corruptFile': 0}], - - # POSIX, quitOnError - [{'debug': 'POSIX quitOnError', - 'quitOnError': 1}], - - # POSIX, quitOnError random - [{'debug': 'POSIX quitOnError random', - 'randomOffset': 1, - 'checkRead': 0, - 'quitOnError': 1}], - - # POSIX, singleXferAttempt - [{'debug': 'POSIX singleXferAttempt', - 'singleXferAttempt': 1}], - - # POSIX, singleXferAttempt random - [{'debug': 'POSIX singleXferAttempt random', - 'randomOffset': 1, - 'checkRead': 0, - 'singleXferAttempt': 1}], - - # POSIX, setTimeStampSignature - [{'debug': 'POSIX setTimeStampSignature', - 'setTimeStampSignature': 123}], - - # POSIX, setTimeStampSignature random - [{'debug': 'POSIX setTimeStampSignature random', - 'randomOffset': 1, - 'checkRead': 0, - 'setTimeStampSignature': 123}], - - # POSIX, useExistingTestFile [Note: don't follow HDF5 test] - [{'debug': 'POSIX useExistingTestFile', - 'useExistingTestFile': 1}], - - # POSIX, useExistingTestFile random [Note: don't follow HDF5 test] - [{'debug': 'POSIX useExistingTestFile', - 'randomOffset': 1, - 'checkRead': 0, - 'useExistingTestFile': 1}], - - # - # pattern dependent tests - # - - # POSIX, filePerProc - [{'debug': 'POSIX filePerProc', - 'filePerProc': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 1}], - - [{'debug': 'POSIX filePerProc', - 'filePerProc': 1, - 'segmentCount': 1}], - - [{'debug': 'POSIX filePerProc', - 'filePerProc': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 3}], - - [{'debug': 'POSIX filePerProc', - 'filePerProc': 1, - 'segmentCount': 3}], - - # POSIX, sharedFile - [{'debug': 'POSIX sharedFile', - 'filePerProc': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 1}], - - [{'debug': 'POSIX sharedFile', - 'filePerProc': 0, - 'segmentCount': 1}], - - [{'debug': 'POSIX sharedFile', - 'filePerProc': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 3}], - - [{'debug': 'POSIX sharedFile', - 'filePerProc': 0, - 'segmentCount': 3}], - - [{'debug': 'POSIX sharedFile numTasks', - 'filePerProc': 0, - 'numTasks': 2, - 'segmentCount': 3}], - - # - # pattern dependent tests, random - # - - # POSIX, filePerProc random - [{'debug': 'POSIX filePerProc random', - 'filePerProc': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 1}], - - [{'debug': 'POSIX filePerProc random', - 'filePerProc': 1, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 1}], - - [{'debug': 'POSIX filePerProc random', - 'filePerProc': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 3}], - - [{'debug': 'POSIX filePerProc random', - 'filePerProc': 1, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 3}], - - # POSIX, sharedFile random - [{'debug': 'POSIX sharedFile random', - 'filePerProc': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 1}], - - [{'debug': 'POSIX sharedFile random', - 'filePerProc': 0, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 1}], - - [{'debug': 'POSIX sharedFile random', - 'filePerProc': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 3}], - - [{'debug': 'POSIX sharedFile random', - 'filePerProc': 0, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 3}], - - [{'debug': 'POSIX sharedFile numTasks random', - 'filePerProc': 0, - 'numTasks': 2, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 3}] - ] - - MPIIO_TESTS = [ - # - # pattern independent tests - # - - # MPIIO, repetitions - [{'debug': 'MPIIO repetitions', - 'api': 'MPIIO', - 'repetitions': 1}], - [{'debug': 'MPIIO repetitions', - 'api': 'MPIIO', - 'repetitions': 3}], - [{'debug': 'MPIIO repetitions random', - 'api': 'MPIIO', - 'randomOffset': 1, - 'checkRead': 0, - 'repetitions': 3}], - - # MPIIO, multiFile - [{'debug': 'MPIIO multiFile', - 'api': 'MPIIO', - 'repetitions': 3, - 'multiFile': 1}], - - # MPIIO, multiFile random - [{'debug': 'MPIIO multiFile random', - 'api': 'MPIIO', - 'repetitions': 3, - 'randomOffset': 1, - 'checkRead': 0, - 'multiFile': 1}], - - # MPIIO, remove file - [{'debug': 'MPIIO remove file', - 'api': 'MPIIO', - 'keepFile': 0}], - - # MPIIO, remove file random - [{'debug': 'MPIIO remove file', - 'api': 'MPIIO', - 'randomOffset': 1, - 'checkRead': 0, - 'keepFile': 0}], - - # MPIIO, remove file with error - [{'debug': 'MPIIO remove file with error', - 'api': 'MPIIO', - 'keepFileWithError': 0}], - - # MPIIO, remove file with error random - [{'debug': 'MPIIO remove file with error random', - 'api': 'MPIIO', - 'randomOffset': 1, - 'checkRead': 0, - 'keepFileWithError': 0}], - - # MPIIO, deadline for stonewalling - [{'debug': 'MPIIO deadlineForStonewalling', - 'api': 'MPIIO', - 'testFile': test.DefaultTest()['testFile'] + '.stonewall', - 'blockSize': GIBIBYTE, - 'readFile': 0, - 'checkWrite': 0, - 'checkRead': 0, - 'deadlineForStonewalling':1}], - - # MPIIO, deadline for stonewalling random - [{'debug': 'MPIIO deadlineForStonewalling random', - 'api': 'MPIIO', - 'testFile': test.DefaultTest()['testFile'] + '.stonewall', - 'blockSize': GIBIBYTE, - 'readFile': 0, - 'checkWrite': 0, - 'randomOffset': 1, - 'checkRead': 0, - 'deadlineForStonewalling':1}], - - # MPIIO, max time duration - [{'debug': 'MPIIO maxTimeDuration', - 'api': 'MPIIO', - 'maxTimeDuration':1}], - - # MPIIO, max time duration random - [{'debug': 'MPIIO maxTimeDuration random', - 'api': 'MPIIO', - 'randomOffset': 1, - 'checkRead': 0, - 'maxTimeDuration':1}], - - # MPIIO, quitOnError - [{'debug': 'MPIIO quitOnError', - 'api': 'MPIIO', - 'quitOnError': 1}], - - # MPIIO, quitOnError random - [{'debug': 'MPIIO quitOnError random', - 'api': 'MPIIO', - 'randomOffset': 1, - 'checkRead': 0, - 'quitOnError': 1}], - - # MPIIO, multiple file names, ssf - [{'debug': 'MPIIO multiple file names ssf', - 'api': 'MPIIO', - 'testFile': testDir + '/f1@' + testDir + '/f2'}], - - # MPIIO, multiple file names, ssf random - [{'debug': 'MPIIO multiple file names ssf random', - 'api': 'MPIIO', - 'randomOffset': 1, - 'checkRead': 0, - 'testFile': testDir + '/f1@' + testDir + '/f2'}], - - # MPIIO, multiple file names, fpp - [{'debug': 'MPIIO multiple file names fpp', - 'api': 'MPIIO', - 'filePerProc': 1, - 'testFile': testDir + '/f1@' + testDir + '/f2'}], - - # MPIIO, multiple file names, fpp random - [{'debug': 'MPIIO multiple file names fpp random', - 'api': 'MPIIO', - 'filePerProc': 1, - 'randomOffset': 1, - 'checkRead': 0, - 'testFile': testDir + '/f1@' + testDir + '/f2'}], - - # MPIIO, corruptFile - [{'debug': 'MPIIO corruptFile', - 'api': 'MPIIO', - 'testFile': test.DefaultTest()['testFile'], - 'filePerProc': 0, - 'corruptFile': 1}], - - # MPIIO, corruptFile random - [{'debug': 'MPIIO corruptFile random', - 'api': 'MPIIO', - 'testFile': test.DefaultTest()['testFile'], - 'filePerProc': 0, - 'randomOffset': 1, - 'checkRead': 0, - 'corruptFile': 1}], - - # MPIIO, corruptFile - [{'debug': 'MPIIO corruptFile filePerProc', - 'api': 'MPIIO', - 'filePerProc': 1, - 'corruptFile': 1}], - - # MPIIO, corruptFile random - [{'debug': 'MPIIO corruptFile filePerProc random', - 'api': 'MPIIO', - 'filePerProc': 1, - 'randomOffset': 1, - 'checkRead': 0, - 'corruptFile': 1}], - - # MPIIO, useExistingTestFile - [{'debug': 'MPIIO useExistingTestFile', - 'api': 'MPIIO', - 'useExistingTestFile': 0, - 'filePerProc': 0, - 'corruptFile': 0}], - - # MPIIO, useExistingTestFile random - [{'debug': 'MPIIO useExistingTestFile random', - 'api': 'MPIIO', - 'useExistingTestFile': 0, - 'filePerProc': 0, - 'randomOffset': 1, - 'checkRead': 0, - 'corruptFile': 0}], - - # MPIIO, preallocate - [{'debug': 'MPIIO preallocate', - 'api': 'MPIIO', - 'preallocate': 1}], - - # MPIIO, showHints - [{'debug': 'MPIIO showHints', - 'api': 'MPIIO', - 'showHints': 1}], - - # MPIIO, showHints w/hintsFileName - [{'debug': 'MPIIO showHints w/hintsFileName', - 'api': 'MPIIO', - 'hintsFileName': '/g/g0/loewe/IOR/test/hintsFile', - 'showHints': 1}], - - # MPIIO, setTimeStampSignature - [{'debug': 'MPIIO setTimeStampSignature', - 'api': 'MPIIO', - 'setTimeStampSignature': 123}], - - # MPIIO, setTimeStampSignature random - [{'debug': 'MPIIO setTimeStampSignature random', - 'api': 'MPIIO', - 'randomOffset': 1, - 'checkRead': 0, - 'setTimeStampSignature': 123}], - - # - # pattern dependent tests - # - - # MPIIO, independent - [{'debug': 'MPIIO independent', - 'api': 'MPIIO', - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 1}], - - [{'debug': 'MPIIO independent', - 'api': 'MPIIO', - 'collective': 0, - 'segmentCount': 1}], - - [{'debug': 'MPIIO independent', - 'api': 'MPIIO', - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 3}], - - [{'debug': 'MPIIO independent', - 'api': 'MPIIO', - 'collective': 0, - 'segmentCount': 3}], - - [{'debug': 'MPIIO independent numTasks', - 'api': 'MPIIO', - 'numTasks': 2, - 'collective': 0, - 'segmentCount': 3}], - - # MPIIO, independent random - [{'debug': 'MPIIO independent random', - 'api': 'MPIIO', - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 1}], - - [{'debug': 'MPIIO independent random', - 'api': 'MPIIO', - 'collective': 0, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 1}], - - [{'debug': 'MPIIO independent random', - 'api': 'MPIIO', - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 3}], - - [{'debug': 'MPIIO independent random', - 'api': 'MPIIO', - 'collective': 0, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 3}], - - [{'debug': 'MPIIO independent numTasks random', - 'api': 'MPIIO', - 'numTasks': 2, - 'collective': 0, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 3}], - - # MPIIO, collective - [{'debug': 'MPIIO collective', - 'api': 'MPIIO', - 'collective': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 1}], - - [{'debug': 'MPIIO collective', - 'api': 'MPIIO', - 'collective': 1, - 'segmentCount': 1}], - - [{'debug': 'MPIIO collective', - 'api': 'MPIIO', - 'collective': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 3}], - - [{'debug': 'MPIIO collective', - 'api': 'MPIIO', - 'collective': 1, - 'segmentCount': 3}], - - [{'debug': 'MPIIO collective numTasks', - 'api': 'MPIIO', - 'numTasks': 2, - 'collective': 1, - 'segmentCount': 3}], - - # MPIIO, independent, useFileView - [{'debug': 'MPIIO independent useFileView', - 'api': 'MPIIO', - 'useFileView': 1, - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 1}], - - [{'debug': 'MPIIO independent useFileView', - 'api': 'MPIIO', - 'useFileView': 1, - 'collective': 0, - 'segmentCount': 1}], - - [{'debug': 'MPIIO independent useFileView', - 'api': 'MPIIO', - 'useFileView': 1, - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 3}], - - [{'debug': 'MPIIO independent useFileView', - 'api': 'MPIIO', - 'useFileView': 1, - 'collective': 0, - 'segmentCount': 3}], - - [{'debug': 'MPIIO independent useFileView numTasks', - 'api': 'MPIIO', - 'useFileView': 1, - 'numTasks': 2, - 'collective': 0, - 'segmentCount': 3}], - - # MPIIO, collective, useFileView - [{'debug': 'MPIIO collective useFileView', - 'api': 'MPIIO', - 'useFileView': 1, - 'collective': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 1}], - - [{'debug': 'MPIIO collective useFileView', - 'api': 'MPIIO', - 'useFileView': 1, - 'collective': 1, - 'segmentCount': 1}], - - [{'debug': 'MPIIO collective useFileView', - 'api': 'MPIIO', - 'useFileView': 1, - 'collective': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 3}], - - [{'debug': 'MPIIO collective useFileView', - 'api': 'MPIIO', - 'useFileView': 1, - 'collective': 1, - 'segmentCount': 3}], - - [{'debug': 'MPIIO collective useFileView numTasks', - 'api': 'MPIIO', - 'useFileView': 1, - 'numTasks': 2, - 'collective': 1, - 'segmentCount': 3}], - - # MPIIO, independent, filePerProc - [{'debug': 'MPIIO independent filePerProc', - 'api': 'MPIIO', - 'filePerProc': 1, - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 1}], - - [{'debug': 'MPIIO independent filePerProc', - 'api': 'MPIIO', - 'filePerProc': 1, - 'collective': 0, - 'segmentCount': 1}], - - [{'debug': 'MPIIO independent filePerProc', - 'api': 'MPIIO', - 'filePerProc': 1, - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 3}], - - [{'debug': 'MPIIO independent filePerProc', - 'api': 'MPIIO', - 'filePerProc': 1, - 'collective': 0, - 'segmentCount': 3}], - - [{'debug': 'MPIIO independent filePerProc numTasks', - 'api': 'MPIIO', - 'filePerProc': 1, - 'numTasks': 2, - 'collective': 0, - 'segmentCount': 3}], - - # MPIIO, independent, filePerProc random - [{'debug': 'MPIIO independent filePerProc random', - 'api': 'MPIIO', - 'filePerProc': 1, - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 1}], - - [{'debug': 'MPIIO independent filePerProc random', - 'api': 'MPIIO', - 'filePerProc': 1, - 'collective': 0, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 1}], - - [{'debug': 'MPIIO independent filePerProc random', - 'api': 'MPIIO', - 'filePerProc': 1, - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 3}], - - [{'debug': 'MPIIO independent filePerProc random', - 'api': 'MPIIO', - 'filePerProc': 1, - 'collective': 0, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 3}], - - [{'debug': 'MPIIO independent filePerProc numTasks random', - 'api': 'MPIIO', - 'filePerProc': 1, - 'numTasks': 2, - 'collective': 0, - 'randomOffset': 1, - 'checkRead': 0, - 'segmentCount': 3}], - - # MPIIO, collective, filePerProc - [{'debug': 'MPIIO collective filePerProc', - 'api': 'MPIIO', - 'filePerProc': 1, - 'collective': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 1}], - - [{'debug': 'MPIIO collective filePerProc', - 'api': 'MPIIO', - 'filePerProc': 1, - 'collective': 1, - 'segmentCount': 1}], - - [{'debug': 'MPIIO collective filePerProc', - 'api': 'MPIIO', - 'filePerProc': 1, - 'collective': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 3}], - - [{'debug': 'MPIIO collective filePerProc', - 'api': 'MPIIO', - 'filePerProc': 1, - 'collective': 1, - 'segmentCount': 3}], - - [{'debug': 'MPIIO collective filePerProc numTasks', - 'api': 'MPIIO', - 'filePerProc': 1, - 'numTasks': 2, - 'collective': 1, - 'segmentCount': 3}], - - # MPIIO, independent, filePerProc, useFileView - [{'debug': 'MPIIO independent filePerProc useFileView', - 'api': 'MPIIO', - 'filePerProc': 1, - 'useFileView': 1, - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 1}], - - [{'debug': 'MPIIO independent filePerProc useFileView', - 'api': 'MPIIO', - 'filePerProc': 1, - 'useFileView': 1, - 'collective': 0, - 'segmentCount': 1}], - - [{'debug': 'MPIIO independent filePerProc useFileView', - 'api': 'MPIIO', - 'filePerProc': 1, - 'useFileView': 1, - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 3}], - - [{'debug': 'MPIIO independent filePerProc useFileView', - 'api': 'MPIIO', - 'filePerProc': 1, - 'useFileView': 1, - 'collective': 0, - 'segmentCount': 3}], - - [{'debug': 'MPIIO independent filePerProc useFileView numTasks', - 'api': 'MPIIO', - 'filePerProc': 1, - 'numTasks': 2, - 'useFileView': 1, - 'collective': 0, - 'segmentCount': 3}], - - # MPIIO, collective, filePerProc, useFileView - [{'debug': 'MPIIO collective filePerProc useFileView', - 'api': 'MPIIO', - 'filePerProc': 1, - 'useFileView': 1, - 'collective': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 1}], - - [{'debug': 'MPIIO collective filePerProc useFileView', - 'api': 'MPIIO', - 'filePerProc': 1, - 'useFileView': 1, - 'collective': 1, - 'segmentCount': 1}], - - [{'debug': 'MPIIO collective filePerProc useFileView', - 'api': 'MPIIO', - 'filePerProc': 1, - 'useFileView': 1, - 'collective': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 3}], - - [{'debug': 'MPIIO collective filePerProc useFileView', - 'api': 'MPIIO', - 'filePerProc': 1, - 'useFileView': 1, - 'collective': 1, - 'segmentCount': 3}], - - [{'debug': 'MPIIO collective filePerProc useFileView numTasks', - 'api': 'MPIIO', - 'filePerProc': 1, - 'numTasks': 2, - 'useFileView': 1, - 'collective': 1, - 'segmentCount': 3}] - ] - - HDF5_TESTS = [ - # - # pattern independent tests - # - - # HDF5, repetitions - [{'debug': 'HDF5 repetitions', - 'api': 'HDF5', - 'repetitions': 1}], - [{'debug': 'HDF5 repetitions', - 'api': 'HDF5', - 'repetitions': 3}], - - # HDF5, multiFile - [{'debug': 'HDF5 multiFile', - 'api': 'HDF5', - 'repetitions': 3, - 'multiFile': 1}], - - # HDF5, useExistingTestFile [Note: this must follow HDF5 test] - [{'debug': 'HDF5 useExistingTestFile', - 'api': 'HDF5', - 'useExistingTestFile': 1}], - - # HDF5, remove file - [{'debug': 'HDF5 remove file', - 'api': 'HDF5', - 'keepFile': 0}], - - # HDF5, remove file with error - [{'debug': 'HDF5 remove file with error', - 'api': 'HDF5', - 'keepFileWithError': 0}], - - # HDF5, deadline for stonewalling - [{'debug': 'HDF5 deadlineForStonewalling', - 'api': 'HDF5', - 'testFile': test.DefaultTest()['testFile'] + '.stonewall', - 'blockSize': GIBIBYTE/4, - 'readFile': 0, - 'checkWrite': 0, - 'checkRead': 0, - 'deadlineForStonewalling':1}], - - # HDF5, max time duration - [{'debug': 'HDF5 maxTimeDuration', - 'api': 'HDF5', - 'maxTimeDuration':1}], - - # HDF5, quitOnError - [{'debug': 'HDF5 quitOnError', - 'api': 'HDF5', - 'quitOnError': 1}], - - # HDF5, multiple file names, ssf - [{'debug': 'HDF5 multiple file names ssf', - 'api': 'HDF5', - 'testFile': testDir + '/f1@' + testDir + '/f2'}], - - # HDF5, multiple file names, fpp - [{'debug': 'HDF5 multiple file names fpp', - 'api': 'HDF5', - 'filePerProc': 1, - 'testFile': testDir + '/f1@' + testDir + '/f2'}], - - # HDF5, corruptFile - [{'debug': 'HDF5 corruptFile', - 'api': 'HDF5', - 'testFile': test.DefaultTest()['testFile'], - 'filePerProc': 0, - 'corruptFile': 1}], - - # HDF5, corruptFile - [{'debug': 'HDF5 corruptFile filePerProc', - 'api': 'HDF5', - 'filePerProc': 1, - 'corruptFile': 1}], - - # HDF5, setTimeStampSignature - [{'debug': 'HDF5 setTimeStampSignature', - 'api': 'HDF5', - 'setTimeStampSignature': 123, - 'filePerProc': 0, - 'corruptFile': 0}], - - # HDF5, setAlignment - [{'debug': 'HDF5 setAlignment', - 'api': 'HDF5', - 'setAlignment': '4m'}], - - # HDF5, showHints - [{'debug': 'HDF5 showHints', - 'api': 'HDF5', - 'showHints': 0}], # WEL: omit this test until - # showHints works - - # HDF5, showHints w/hintsFileName - [{'debug': 'HDF5 showHints w/hintsFileName', - 'api': 'HDF5', - 'hintsFileName': '/g/g0/loewe/IOR/test/hintsFile', - 'showHints': 0}], # WEL: omit this test until - # showHints works - - # HDF5, noFill - [{'debug': 'HDF5 noFill', - 'api': 'HDF5', - 'noFill': 0}], # WEL: omit this test until - # noFill is standard - - # - # pattern dependent tests - # - - # HDF5, independent - [{'debug': 'HDF5 independent', - 'api': 'HDF5', - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 1}], - - [{'debug': 'HDF5 independent', - 'api': 'HDF5', - 'collective': 0, - 'segmentCount': 1}], - - [{'debug': 'HDF5 independent', - 'api': 'HDF5', - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 3}], - - [{'debug': 'HDF5 independent', - 'api': 'HDF5', - 'collective': 0, - 'segmentCount': 3}], - - [{'debug': 'HDF5 independent numTasks', - 'api': 'HDF5', - 'numTasks': 2, - 'collective': 0, - 'segmentCount': 3}], - - # HDF5, collective - [{'debug': 'HDF5 collective', - 'api': 'HDF5', - 'collective': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 1}], - - [{'debug': 'HDF5 collective', - 'api': 'HDF5', - 'collective': 1, - 'segmentCount': 1}], - - [{'debug': 'HDF5 collective', - 'api': 'HDF5', - 'collective': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 3}], - - [{'debug': 'HDF5 collective', - 'api': 'HDF5', - 'collective': 1, - 'segmentCount': 3}], - - [{'debug': 'HDF5 collective numTasks', - 'api': 'HDF5', - 'numTasks': 2, - 'collective': 1, - 'segmentCount': 3}] - ] - - NCMPI_TESTS = [ - # - # pattern independent tests - # - - # NCMPI, repetitions - [{'debug': 'NCMPI repetitions', - 'api': 'NCMPI', - 'repetitions': 1}], - [{'debug': 'NCMPI repetitions', - 'api': 'NCMPI', - 'repetitions': 3}], - - # NCMPI, multiFile - [{'debug': 'NCMPI multiFile', - 'api': 'NCMPI', - 'repetitions': 3, - 'multiFile': 1}], - - # NCMPI, deadline for stonewalling - [{'debug': 'NCMPI deadlineForStonewalling', - 'api': 'NCMPI', - 'testFile': test.DefaultTest()['testFile'] + '.stonewall', - 'blockSize': GIBIBYTE/4, - 'readFile': 0, - 'checkWrite': 0, - 'checkRead': 0, - 'deadlineForStonewalling':1}], - - # NCMPI, max time duration - [{'debug': 'NCMPI maxTimeDuration', - 'api': 'NCMPI', - 'maxTimeDuration':1}], - - # NCMPI, remove file - [{'debug': 'NCMPI remove file', - 'api': 'NCMPI', - 'keepFile': 0}], - - # NCMPI, remove file with error - [{'debug': 'NCMPI remove file with error', - 'api': 'NCMPI', - 'keepFileWithError': 0}], - - # NCMPI, quitOnError - [{'debug': 'NCMPI quitOnError', - 'api': 'NCMPI', - 'quitOnError': 1}], - - # NCMPI, multiple file names, ssf - [{'debug': 'NCMPI multiple file names ssf', - 'api': 'NCMPI', - 'testFile': testDir + '/f1@' + testDir + '/f2'}], - - # NCMPI, corruptFile - [{'debug': 'NCMPI corruptFile', - 'api': 'NCMPI', - 'testFile': test.DefaultTest()['testFile'], - 'corruptFile': 1}], - - # NCMPI, setTimeStampSignature - [{'debug': 'NCMPI setTimeStampSignature', - 'api': 'NCMPI', - 'setTimeStampSignature': 123, - 'corruptFile': 0}], - - # NCMPI, useExistingTestFile [Note: this must follow NCMPI test] - [{'debug': 'NCMPI useExistingTestFile', - 'api': 'NCMPI', - 'useExistingTestFile': 1}], - - # - # pattern dependent tests - # - - # NCMPI, independent - [{'debug': 'NCMPI independent', - 'api': 'NCMPI', - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 1}], - - [{'debug': 'NCMPI independent', - 'api': 'NCMPI', - 'collective': 0, - 'segmentCount': 1}], - - [{'debug': 'NCMPI independent', - 'api': 'NCMPI', - 'collective': 0, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 3}], - - [{'debug': 'NCMPI independent', - 'api': 'NCMPI', - 'collective': 0, - 'segmentCount': 3}], - - [{'debug': 'NCMPI independent numTasks', - 'api': 'NCMPI', - 'numTasks': 2, - 'collective': 0, - 'segmentCount': 3}], - - # NCMPI, collective - [{'debug': 'NCMPI collective', - 'api': 'NCMPI', - 'collective': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 1}], - - [{'debug': 'NCMPI collective', - 'api': 'NCMPI', - 'collective': 1, - 'segmentCount': 1}], - - [{'debug': 'NCMPI collective', - 'api': 'NCMPI', - 'collective': 1, - 'blockSize': IOR_SIZE_T, - 'transferSize': IOR_SIZE_T, - 'segmentCount': 3}], - - [{'debug': 'NCMPI collective', - 'api': 'NCMPI', - 'collective': 1, - 'segmentCount': 3}], - - [{'debug': 'NCMPI collective numTasks', - 'api': 'NCMPI', - 'numTasks': 2, - 'collective': 1, - 'segmentCount': 3}] - ] - - PassTests = [] - if OS == "AIX": - PassTests = PassTests + POSIX_TESTS - PassTests = PassTests + MPIIO_TESTS - PassTests = PassTests + HDF5_TESTS - PassTests = PassTests + NCMPI_TESTS - elif OS == "Linux": - PassTests = PassTests + POSIX_TESTS - PassTests = PassTests + MPIIO_TESTS - #PassTests = PassTests + HDF5_TESTS - #PassTests = PassTests + NCMPI_TESTS - else: - PassTests = [ - [{'debug': 'failure to determine OS'}] - ] - - FailTests = [ - [{'debug': 'no tests should fail'}] - ] - -################################################################################ -################################################################################ -# # -# E N D O F T E S T S # -# # -################################################################################ -################################################################################ - # WEL debugging: if 0 == 0: PassTests = [ [{'debug': 'debug test'}] ] - - if expectation == PASS: - if testNumber == RETURN_TOTAL_TESTS: - return len(PassTests) - else: - return PassTests[testNumber] - else: # expectation == FAIL - if testNumber == RETURN_TOTAL_TESTS: - return len(FailTests) - else: - return FailTests[testNumber] - - - ################### - # run test script # - ################### - def RunScript(self, nodes, procs): - if OS == "AIX": - command = "poe " + executable + " -f " + scriptFile + \ - " -nodes " + str(nodes) + " -procs " + str(procs) + \ - " -rmpool systest -labelio no -retry wait" - elif OS == "Linux": - command = "srun -N " + str(procs) + " -n " + str(procs) + \ - " -ppdebug " + executable + " -f " + scriptFile - else: - command = "unable to run " + executable + " -f " + scriptFile - if debug == TRUE: - Flush2File(command) - else: - childIn, childOut = os.popen4(command) - childIn.close() - while 1: - line = childOut.readline() - if line == '': break - Flush2File(line[:-1]) - childOut.close() - return - - -########################## -# create subsets of list # -########################## -def ListSubsets(tests, size): - listOfSubsets = [] - start = end = 0 - totalTestsSize = len(tests) - for i in range(0, (totalTestsSize / size) + 1): - if end >= totalTestsSize: - break - end = end + size - listOfSubsets.append(tests[start:end]) - start = end - return listOfSubsets - - -################# -# flush to file # -################# -def Flush2File(string): - resultsFile.write(string + '\n') - resultsFile.flush() - - -################################## -# replace blanks with underscore # -################################## -def UnderScore(string): - uString = string - for i in range(0, len(uString)): - if uString[i] == ' ': - uString = uString[:i] + '_' + uString[i+1:] - return(uString) - - -############################# -# grep for keywords in file # -############################# -def grepForKeywords(keywords, resultsFileName): - # create pattern for grep - pattern = "\"" - for i in range(len(keywords)): - pattern = pattern + keywords[i] - if i < len(keywords)-1: - pattern = pattern + '|' - pattern = pattern + "\"" - - # grep for pattern in file - resultsFileNameTmp = resultsFileName + ".tmp" - cmd = "grep -i -E " + pattern + " " + resultsFileName \ - + " >> " + resultsFileNameTmp + " 2>&1" - os.system(cmd) - cmd = "cat " + resultsFileNameTmp + " >> " + resultsFileName - os.system(cmd) - cmd = "rm -f " + resultsFileNameTmp - os.system(cmd) - - -################################################################################ -# main # -################################################################################ -resultsFileName = "./test-results.txt-" + \ - os.popen("date +%m.%d.%y").read()[:-1] -resultsFile = open(resultsFileName, "w") -OS = os.popen("uname -s").read()[:-1] -test = Test() -testNumber = 0 - -#environment variables -nodes = 1 -proccnt = [1, 3] - -Flush2File("TESTING IOR C CODE") - -# loop through different processors counts -for proc in proccnt: - - # first run all expected-PASS test, then the FAILs - for testType in (PASS, FAIL): - - # test type info - if (testType == PASS): - Flush2File("\n*** STARTING EXPECTED P A S S TESTS (PROC=" \ - + str(proc) + ") ***") - else: - Flush2File("\n*** STARTING EXPECTED F A I L TESTS (PROC=" \ - + str(proc) + ") ***") - - # loop through all tests for test type - totalTests = range(test.Tests(testType, RETURN_TOTAL_TESTS)) - firstTest = TRUE - for testSubset in ListSubsets(totalTests, TEST_SUBSET_SIZE): - for i in testSubset: - if (firstTest == TRUE): - Flush2File("\n\n*** Setting up tests ***") - firstTest = FALSE - if (i % 10 == 0 and i != 0): - Flush2File("finished " + str(i) + " tests") - sys.stdout.flush() - # unless an expected fail test, only open a single script - # create script file name - if (testType == PASS): - scriptFile = scriptFileBase + '.' + str(proc) + '.PASS' - else: - scriptFile = scriptFileBase + '.' + str(proc) + '.FAIL' - - scriptFile = scriptFile + '-TESTS_' + str(testSubset[0:1][0]) \ - + '-' + str(testSubset[len(testSubset)-1:][0]) - if ((i % TEST_SUBSET_SIZE == 0) or (testType == FAIL)): - os.system("rm -f " + scriptFile) - script = open(scriptFile, "a") - script.write("IOR START" + "\n") - - # start with a default test, then modify - testValues = test.DefaultTest() - - # loop through all changes to the default script - for j in test.Tests(testType, i)[TEST_SETTINGS].keys(): - testValues[j] = test.Tests(testType, i)[TEST_SETTINGS][j] - - testNumber = testNumber + 1 - testValues['debug'] = UnderScore("Test No. " + \ - str(testNumber) + ": " + \ - testValues['debug']) - # write test information to script file - for entry in testValues.keys(): - if (str(testValues[entry]) != ''): - script.write("\t" + entry + "=" + - str(testValues[entry]) + "\n") - script.write("RUN" + "\n") - - # unless an expected fail test, only close a single script - if ((i == testSubset[len(testSubset)-1:][0]) \ - or (testType == FAIL)): - # create tail, close file - script.write("IOR STOP" + "\n") - script.close() - if (testType == FAIL): - Flush2File("finished 1 test") - else: - Flush2File("finished %d test%s" % ((i + 1), "s"[i==1:])) - firstTest = TRUE - - # display test info for failing test (note that the - # test fails before a description is displayed) - if testType == FAIL: - Flush2File("\n\t================================\n\n") - Flush2File("*** DEBUG MODE ***") - Flush2File("*** " + str(testValues['debug']) + \ - " ***") - Flush2File("") - - # run - os.system ("rm -rf " + testDir) - os.system("mkdir " + testDir) - test.RunScript(nodes, proc) - os.system ("rm -rf " + testDir) - if 0 == 0: os.system("rm -f " + scriptFile) # run scripts - -Flush2File("\nFINISHED TESTING IOR C CODE") -Flush2File("\nRESULTS:") -resultsFile.close() -grepForKeywords(["warn", "fail", "error", "Test_No"], resultsFileName) diff --git a/scripts/run_script.linux b/testing/basic-tests.sh similarity index 100% rename from scripts/run_script.linux rename to testing/basic-tests.sh diff --git a/testing/hintsFile b/testing/hintsFile deleted file mode 100755 index 6931afb..0000000 --- a/testing/hintsFile +++ /dev/null @@ -1,2 +0,0 @@ -IOR_HINT__MPI__unrecognizedHint=true -IOR_HINT__MPI__IBM_largeblock_io=true diff --git a/testing/timestamp.cfg b/testing/timestamp.cfg deleted file mode 100644 index 21a9808..0000000 --- a/testing/timestamp.cfg +++ /dev/null @@ -1,28 +0,0 @@ -# This tests the checks of read and write for correctness -# Run with multiple processes, at least 3 -# You may set the environment variable IOR_FAKE_TASK_PER_NODES=3 and run it on one node -# Example: IOR_FAKE_TASK_PER_NODES=3 mpiexec -n 3 ./src/ior -f testing/timestamp.cfg - -IOR START -reorderTasksConstant=1 -repetitions=1 -storeFileOffset=0 -segmentCount=1 -verbose=0 -fsync=0 -checkWrite=1 -blockSize=16 -setTimeStampSignature=1511817315 -checkRead=1 -readFile=1 -filePerProc=0 -writeFile=1 -api=POSIX -transferSize=16 -intraTestBarriers=0 -testFile=/tmp/test -RUN - -storeFileOffset=1 -RUN -IOR STOP From aca75a30373f122c986038563a28a1afc6cc6b45 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Fri, 6 Jul 2018 22:19:25 +0100 Subject: [PATCH 08/80] Simple testscript based on preexisting tests. --- testing/basic-tests.sh | 132 +++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 77 deletions(-) diff --git a/testing/basic-tests.sh b/testing/basic-tests.sh index 42d444f..ecc7f68 100755 --- a/testing/basic-tests.sh +++ b/testing/basic-tests.sh @@ -1,93 +1,71 @@ -#!/bin/bash -x +#!/bin/bash -#PBS -N IOR -#PBS -j oe -#PBS -q batch -#PBS -A stf006 -#PBS -V -#PBS -l walltime=0:60:00,nodes=8:ppn=2 +# Test script for basic IOR functionality testing various patterns +# It is kept as simple as possible and outputs the parameters used such that any test can be rerun easily. -VERS=IOR-2.10.1 -WORK=/tmp/work/${USER} -echo $PBS_O_WORKDIR +# You can override the defaults by setting the variables before invoking the script, or simply set them here... +# Example: export IOR_EXTRA="-v -v -v" -cd /ccs/proj/quadcore -tar -czvf ${WORK}/${VERS}.tar.gz ./${VERS} -cd ${WORK} -rm -fr ./${VERS} -tar -xzvf ${WORK}/${VERS}.tar.gz -cd ${WORK}/${VERS} -gmake clean -gmake mpiio -EXEC=${WORK}/${VERS}/src/C/IOR -IODIR=/tmp/work/swh13/test_files_x -cd ${WORK}/${VERS}/tests +IOR_MPIRUN=${IOR_MPIRUN:-mpiexec -np} +IOR_EXEC=${IOR_EXEC:-./build/src/ior} +IOR_OUT=${IOR_OUT:-./build/test} +IOR_EXTRA=${IOR_EXTRA:-./build/test} # Add global options like verbosity -which mpirun +################################################################################ -rm -fr $IODIR -mkdir $IODIR +mkdir -p ${IOR_OUT} -let "w=128" -let "s=1024*1024" -let "i=3" +## Sanity check -MPIRUN="mpirun -np" +if [[ ! -e ${IOR_OUT} ]]; then + echo "Could not create output dir ${IOR_OUT}" + exit 1 +fi -RESULTS="." +if [[ ! -e $IOR_EXEC ]]; then + echo "IOR Executable \"$IOR_EXEC\" does not exist! Call me from the root directory!" + exit 1 +fi -let "tid=1" -XFERS="1048576 262144 32768 4096 1024" -XFERS="262144" -for xfer in `echo $XFERS` -do - let "n=8" -until [ "$n" -gt 8 ] -do - let "m=$n/4" - #TESTS="POSIX MPIIO HDF5 NCMPI" - TESTS="POSIX MPIIO" - for test in `echo $TESTS` - do - runid="p$n.$xfer.${test}" - date +ERRORS=0 # Number of errors detected while running +I=0 +function TEST(){ + ${IOR_MPIRUN} ${@} ${IOR_EXTRA} 1>${IOR_OUT}/$I 2>&1 + if [[ $? != 0 ]]; then + echo -n "ERR" + ERRORS=$(($ERRORS + 1)) + else + echo -n "OK " + fi + echo " ${IOR_OUT}/${I} ${IOR_MPIRUN} ${@}" + I=$((${I}+1)) +} - V=" " - BLOCKS="1 10 1 10 1 10" - for blocks in `echo $BLOCKS` - do +TEST 1 ${IOR_EXEC} -a POSIX -w -z -F -Y -e -i1 -m -t 100k -b 1000k +TEST 1 ${IOR_EXEC} -a POSIX -w -z -F -k -e -i2 -m -t 100k -b 100k +TEST 1 ${IOR_EXEC} -a POSIX -r -z -F -k -e -i1 -m -t 100k -b 100k - let "block=${xfer} * ${blocks}" +TEST 2 ${IOR_EXEC} -a POSIX -w -z -C -F -k -e -i1 -m -t 100k -b 100k +TEST 2 ${IOR_EXEC} -a POSIX -w -z -C -Q 1 -F -k -e -i1 -m -t 100k -b 100k +TEST 2 ${IOR_EXEC} -a POSIX -r -z -Z -Q 2 -F -k -e -i1 -m -t 100k -b 100k +TEST 2 ${IOR_EXEC} -a POSIX -r -z -Z -Q 3 -X 13 -F -k -e -i1 -m -t 100k -b 100k +TEST 2 ${IOR_EXEC} -a POSIX -w -z -Z -Q 1 -X -13 -F -e -i1 -m -t 100k -b 100k - #fileperproc tests - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -w -z ${V} -F -o $IODIR/testwrite.${runid} -Y -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -w -z ${V} -F -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z ${V} -F -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z -C ${V} -F -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z -C -Q $m ${V} -F -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z -Z -Q $m ${V} -F -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z -Z -Q $m -X 13 ${V} -F -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z -Z -Q $m -X -13 ${V} -F -o $IODIR/testwrite.${runid} -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 +#shared tests +TEST 2 ${IOR_EXEC} -a POSIX -w -z -Y -e -i1 -m -t 100k -b 100k +TEST 2 ${IOR_EXEC} -a POSIX -w -k -e -i1 -m -t 100k -b 100k +TEST 2 ${IOR_EXEC} -a POSIX -r -z -k -e -i1 -m -t 100k -b 100k - #shared tests - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -w -z ${V} -o $IODIR/testwrite.${runid} -Y -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -w ${V} -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z ${V} -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 +#test mutually exclusive options +TEST 2 ${IOR_EXEC} -a POSIX -w -z -k -e -i1 -m -t 100k -b 100k +TEST 2 ${IOR_EXEC} -a POSIX -w -z - -k -e -i1 -m -t 100k -b 100k +TEST 2 ${IOR_EXEC} -a POSIX -w -Z -i1 -m -t 100k -b 100k -d 0.1 - #test mutually exclusive options - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z -C ${V} -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -z -Z ${V} -o $IODIR/testwrite.${runid} -k -e -i${i} -m -t ${xfer} -b ${block} -d 0.1 - ${MPIRUN} $n ${EXEC} -A ${tid} -a ${test} -r -Z -C ${V} -o $IODIR/testwrite.${runid} -i${i} -m -t ${xfer} -b ${block} -d 0.0 - let "tid=$tid + 17" +if [[ ${ERRORS} == 0 ]] ; then + echo "PASSED" +else + echo "Error, check the output files!" +fi - V=$V" -v" - - done #blocks - - date - done #test - let "n = $n * 2" - done #n -done #xfer -exit +exit ${ERRORS} From 7cb659e55d487deece261436cb057eaf5d01b67a Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Fri, 6 Jul 2018 22:27:17 +0100 Subject: [PATCH 09/80] Added default tests to travis. --- .travis.yml | 2 +- README | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index feb304a..49ae8f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,7 @@ install: # GPFS # NOTE: Think GPFS need a license and is therefore not testable with travis. before_script: ./bootstrap -script: ./configure --with-hdf5 && make +script: mkdir build && cd build && ../configure --with-hdf5 && make && cd .. && ./testing/basic-tests.sh # notifications: diff --git a/README b/README index 5ab9493..b5100b2 100755 --- a/README +++ b/README @@ -1,4 +1,4 @@ -This repo now contains both IOR and mdtest. +This repo now contains both IOR and mdtest. [![Build Status](https://travis-ci.org/hpc/ior.svg?branch=master)](https://travis-ci.org/hpc/ior) [See also NOTES.txt] From 98bbafd0d4832c8a7aa77d446d4b660452cced19 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Fri, 6 Jul 2018 22:30:08 +0100 Subject: [PATCH 10/80] Readme renamed to enable markdown and proper build passing item. --- README => README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) rename README => README.md (64%) diff --git a/README b/README.md similarity index 64% rename from README rename to README.md index b5100b2..6cfa992 100755 --- a/README +++ b/README.md @@ -1,9 +1,9 @@ -This repo now contains both IOR and mdtest. [![Build Status](https://travis-ci.org/hpc/ior.svg?branch=master)](https://travis-ci.org/hpc/ior) +# HPC IO Benchmark Repository [![Build Status](https://travis-ci.org/hpc/ior.svg?branch=master)](https://travis-ci.org/hpc/ior) -[See also NOTES.txt] +This repo now contains both IOR and mdtest. +See also NOTES.txt -Building --------- +# Building 0. If "configure" is missing from the top level directory, you probably retrieved this code directly from the repository. @@ -21,3 +21,5 @@ Building 3. Optionally, run "make install". The installation prefix can be changed as an option to the "configure" script. + +4. To run basic functionality tests that we use for continuous integration, see ./testing/ From 2148f68d3649850c29d80c3db7160b3a9f0524c6 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Fri, 6 Jul 2018 22:42:46 +0100 Subject: [PATCH 11/80] Allow to use README.md instead of README. --- Makefile.am | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 60b2aa2..a9daf28 100755 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ SUBDIRS = src doc contrib -EXTRA_DIST = META COPYRIGHT README ChangeLog +EXTRA_DIST = META COPYRIGHT README.md ChangeLog # ACLOCAL_AMFLAGS needed for autoconf < 2.69 ACLOCAL_AMFLAGS = -I config diff --git a/configure.ac b/configure.ac index 4f9461c..d1a066a 100755 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ AC_CONFIG_HEADER([src/config.h]) AC_CANONICAL_HOST # Automake support -AM_INIT_AUTOMAKE([check-news dist-bzip2 gnu no-define]) +AM_INIT_AUTOMAKE([check-news dist-bzip2 gnu no-define foreign]) AM_MAINTAINER_MODE # Checks for programs From de1d3ff266efba8b7444f3cde2172d7e10a48a42 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 00:10:58 +0100 Subject: [PATCH 12/80] Docker local testing of 4 distributions. --- README.md | 6 +++- testing/basic-tests.sh | 3 +- testing/docker/README.md | 10 ++++++ testing/docker/centos6/Dockerfile | 5 +++ testing/docker/centos6/run-test.sh | 44 ++++++++++++++++++++++++ testing/docker/centos7/Dockerfile | 5 +++ testing/docker/centos7/run-test.sh | 42 +++++++++++++++++++++++ testing/docker/prepare.sh | 19 +++++++++++ testing/docker/run-all-tests.sh | 47 ++++++++++++++++++++++++++ testing/docker/ubuntu14.04/Dockerfile | 6 ++++ testing/docker/ubuntu14.04/run-test.sh | 38 +++++++++++++++++++++ testing/docker/ubuntu16.04/Dockerfile | 6 ++++ testing/docker/ubuntu16.04/run-test.sh | 44 ++++++++++++++++++++++++ 13 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 testing/docker/README.md create mode 100644 testing/docker/centos6/Dockerfile create mode 100755 testing/docker/centos6/run-test.sh create mode 100644 testing/docker/centos7/Dockerfile create mode 100755 testing/docker/centos7/run-test.sh create mode 100755 testing/docker/prepare.sh create mode 100755 testing/docker/run-all-tests.sh create mode 100644 testing/docker/ubuntu14.04/Dockerfile create mode 100755 testing/docker/ubuntu14.04/run-test.sh create mode 100644 testing/docker/ubuntu16.04/Dockerfile create mode 100755 testing/docker/ubuntu16.04/run-test.sh diff --git a/README.md b/README.md index 6cfa992..dd9ee1e 100755 --- a/README.md +++ b/README.md @@ -22,4 +22,8 @@ See also NOTES.txt 3. Optionally, run "make install". The installation prefix can be changed as an option to the "configure" script. -4. To run basic functionality tests that we use for continuous integration, see ./testing/ +# Testing + + * To run basic functionality tests that we use for continuous integration, see ./testing/ + * There are docker scripts provided to test various distributions at once. + * See ./testing/docker/ diff --git a/testing/basic-tests.sh b/testing/basic-tests.sh index ecc7f68..4575c7e 100755 --- a/testing/basic-tests.sh +++ b/testing/basic-tests.sh @@ -12,7 +12,6 @@ IOR_OUT=${IOR_OUT:-./build/test} IOR_EXTRA=${IOR_EXTRA:-./build/test} # Add global options like verbosity ################################################################################ - mkdir -p ${IOR_OUT} ## Sanity check @@ -38,7 +37,7 @@ function TEST(){ else echo -n "OK " fi - echo " ${IOR_OUT}/${I} ${IOR_MPIRUN} ${@}" + echo " ${IOR_OUT}/${I} ${IOR_MPIRUN} -o /dev/shm/ior ${@}" I=$((${I}+1)) } diff --git a/testing/docker/README.md b/testing/docker/README.md new file mode 100644 index 0000000..5467285 --- /dev/null +++ b/testing/docker/README.md @@ -0,0 +1,10 @@ +# Docker enabled testing + +This directory contains scripts to run the IOR benchmark testing in various Docker images. +This allows for testing several distributions on a developer machine. + +To setup your test systems run: + ./prepare.sh + +To run all tests for all variants use + ./run-all-tests.sh diff --git a/testing/docker/centos6/Dockerfile b/testing/docker/centos6/Dockerfile new file mode 100644 index 0000000..30712ce --- /dev/null +++ b/testing/docker/centos6/Dockerfile @@ -0,0 +1,5 @@ +FROM centos:6 + +WORKDIR /data +RUN yum install -y mpich openmpi git pkg-config nano gcc bzip2 patch gcc-c++ make mpich-devel openmpi-devel +RUN yum install -y sudo diff --git a/testing/docker/centos6/run-test.sh b/testing/docker/centos6/run-test.sh new file mode 100755 index 0000000..387997c --- /dev/null +++ b/testing/docker/centos6/run-test.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +BUILD="$1" +if [[ $UID == 0 ]]; then + groupadd -g $3 testuser + useradd -r -u $2 -g testuser testuser + sudo -u testuser $0 $1 + exit $? +fi + +ERROR=0 + +function runTest(){ + P=$PATH + FLAVOR="$1" + MPI_DIR="$2" + export IOR_MPIRUN="$3" + + echo $FLAVOR in $BUILD/$FLAVOR + export PATH=$MPI_DIR/bin:$PATH + mkdir -p $BUILD/$FLAVOR + + pushd $BUILD/$FLAVOR > /dev/null + /data/configure || exit 1 + make || exit 1 + + cd /data/ + export IOR_EXEC=$BUILD/$FLAVOR/src/ior + export IOR_OUT=$BUILD/$FLAVOR/test + ./testing/basic-tests.sh + + ERROR=$(($ERROR + $?)) + popd > /dev/null + PATH=$P +} + + +runTest openmpi /usr/lib64/openmpi/ "mpiexec -n" +export MPI_ARGS="" +runTest mpich /usr/lib64/mpich "mpiexec -n" + +#kill -9 %1 + +exit $ERROR diff --git a/testing/docker/centos7/Dockerfile b/testing/docker/centos7/Dockerfile new file mode 100644 index 0000000..db70df4 --- /dev/null +++ b/testing/docker/centos7/Dockerfile @@ -0,0 +1,5 @@ +FROM centos:7 + +WORKDIR /data +RUN yum install -y mpich openmpi git pkg-config nano gcc bzip2 patch gcc-c++ make mpich-devel openmpi-devel +RUN yum install -y sudo diff --git a/testing/docker/centos7/run-test.sh b/testing/docker/centos7/run-test.sh new file mode 100755 index 0000000..088fcb0 --- /dev/null +++ b/testing/docker/centos7/run-test.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +BUILD="$1" + +if [[ $UID == 0 ]]; then + groupadd -g $3 testuser + useradd -r -u $2 -g testuser testuser + sudo -u testuser $0 $1 + exit $? +fi + +ERROR=0 + +function runTest(){ + P=$PATH + FLAVOR="$1" + MPI_DIR="$2" + + echo $FLAVOR in $BUILD/$FLAVOR + mkdir -p $BUILD/$FLAVOR + + pushd $BUILD/$FLAVOR > /dev/null + + export PATH=$MPI_DIR/bin:$PATH + /data/configure || exit 1 + make || exit 1 + + cd /data/ + export IOR_EXEC=$BUILD/$FLAVOR/src/ior + export IOR_OUT=$BUILD/$FLAVOR/test + ./testing/basic-tests.sh + + ERROR=$(($ERROR + $?)) + popd > /dev/null + PATH=$P +} + + +runTest openmpi /usr/lib64/openmpi/ +runTest mpich /usr/lib64/mpich + +exit $ERROR diff --git a/testing/docker/prepare.sh b/testing/docker/prepare.sh new file mode 100755 index 0000000..c8ed22b --- /dev/null +++ b/testing/docker/prepare.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +echo "Checking docker" +docker ps +if [ $? != 0 ] ; then + echo "Error, cannot run docker commands" + groups |grep docker || echo "You are not in the docker group !" + exit 1 +fi + +echo "Building docker containers" + +for IMAGE in $(find -type d | cut -b 3- |grep -v "^$") ; do + docker build -t hpc/ior:$IMAGE $IMAGE + if [ $? != 0 ] ; then + echo "Error building image $IMAGE" + exit 1 + fi +done diff --git a/testing/docker/run-all-tests.sh b/testing/docker/run-all-tests.sh new file mode 100755 index 0000000..30d2deb --- /dev/null +++ b/testing/docker/run-all-tests.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# This script runs the testscript for all supported docker images + +TARGET=../../build-docker +mkdir -p $TARGET + +ARGS="$@" +GID=$(id -g $USER) +OPT="-it --rm -v $PWD/../../:/data/" +ERROR=0 +VERBOSE=0 + +set -- `getopt -u -l "clean" -l verbose -o "" -- "$ARGS"` +test $# -lt 1 && exit 1 +while test $# -gt 0 +do + case "$1" in + --clean) echo "Cleaning build dirs!"; rm -rf $TARGET/* ;; + --verbose) VERBOSE=1 ;; + --) ;; + *) echo "Unknown option $1"; exit 1;; + esac + shift +done + +for IMAGE in $(find -type d | cut -b 3- |grep -v "^$") ; do + echo "RUNNING $IMAGE" + mkdir -p $TARGET/$IMAGE + WHAT="docker run $OPT -h $IMAGE hpc/ior:$IMAGE /data/testing/docker/$IMAGE/run-test.sh /data/build-docker/$IMAGE $UID $GID" + if [[ $VERBOSE == 1 ]] ; then + echo $WHAT + fi + $WHAT 2>$TARGET/$IMAGE/LastTest.log 1>&2 + ERR=$? + ERROR=$(($ERROR+$ERR)) + if [[ $ERR != 0 ]]; then + echo $WHAT + echo "Error, see $TARGET/$IMAGE/LastTest.log" + fi +done + +if [[ $ERROR != 0 ]] ; then + echo "Errors occured!" +else + echo "OK: all tests passed!" +fi diff --git a/testing/docker/ubuntu14.04/Dockerfile b/testing/docker/ubuntu14.04/Dockerfile new file mode 100644 index 0000000..735178b --- /dev/null +++ b/testing/docker/ubuntu14.04/Dockerfile @@ -0,0 +1,6 @@ +FROM ubuntu:14.04 + +WORKDIR /data +RUN apt-get update +RUN apt-get install -y libopenmpi-dev openmpi-bin mpich git pkg-config gcc-4.7 nano make +RUN apt-get install -y sudo diff --git a/testing/docker/ubuntu14.04/run-test.sh b/testing/docker/ubuntu14.04/run-test.sh new file mode 100755 index 0000000..b7084c5 --- /dev/null +++ b/testing/docker/ubuntu14.04/run-test.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +BUILD="$1" +if [[ $UID == 0 ]]; then + groupadd -g $3 testuser + useradd -r -u $2 -g testuser testuser + sudo -u testuser $0 $1 + exit $? +fi +ERROR=0 + +function runTest(){ + FLAVOR="$1" + MPI_DIR="$2" + echo $FLAVOR in $BUILD/$FLAVOR + update-alternatives --set mpi $MPI_DIR + mkdir -p $BUILD/$FLAVOR + + pushd $BUILD/$FLAVOR > /dev/null + /data/configure || exit 1 + make || exit 1 + + #define the alias + ln -sf $(which mpiexec.$FLAVOR) /usr/bin/mpiexec + + cd /data/ + export IOR_EXEC=$BUILD/$FLAVOR/src/ior + export IOR_OUT=$BUILD/$FLAVOR/test + ./testing/basic-tests.sh + + ERROR=$(($ERROR + $?)) + popd > /dev/null +} + +runTest openmpi /usr/lib/openmpi/include +runTest mpich /usr/include/mpich + +exit $ERROR diff --git a/testing/docker/ubuntu16.04/Dockerfile b/testing/docker/ubuntu16.04/Dockerfile new file mode 100644 index 0000000..2583a3a --- /dev/null +++ b/testing/docker/ubuntu16.04/Dockerfile @@ -0,0 +1,6 @@ +FROM ubuntu:16.04 + +WORKDIR /data +RUN apt-get update +RUN apt-get install -y libopenmpi-dev openmpi-bin mpich git pkg-config gcc-5 gcc-4.8 nano +RUN apt-get install -y sudo diff --git a/testing/docker/ubuntu16.04/run-test.sh b/testing/docker/ubuntu16.04/run-test.sh new file mode 100755 index 0000000..950bdaa --- /dev/null +++ b/testing/docker/ubuntu16.04/run-test.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +BUILD="$1" +if [[ $UID == 0 ]]; then + groupadd -g $3 testuser + useradd -r -u $2 -g testuser testuser + sudo -u testuser $0 $1 + exit $? +fi +groupadd -g $3 testuser +useradd -r -u $2 -g testuser testuser + +ERROR=0 + +function runTest(){ + FLAVOR="$1" + MPI_DIR="$2" + export IOR_MPIRUN="$3" + echo $FLAVOR in $BUILD/$FLAVOR + update-alternatives --set mpi $MPI_DIR + mkdir -p $BUILD/$FLAVOR + + pushd $BUILD/$FLAVOR > /dev/null + /data/configure || exit 1 + make || exit 1 + + #define the alias + ln -sf $(which mpiexec.$FLAVOR) /usr/bin/mpiexec + + cd /data/ + export IOR_EXEC=$BUILD/$FLAVOR/src/ior + export IOR_OUT=$BUILD/$FLAVOR/test + + ./testing/basic-tests.sh + + ERROR=$(($ERROR + $?)) + popd > /dev/null +} + +export MPI_ARGS="" +runTest openmpi /usr/lib/openmpi/include "mpiexec -n" +runTest mpich /usr/include/mpich "mpiexec -n" + +exit $ERROR From b5e7212d84dd15b290dbe4ec62ffbd1ee4d19b90 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 00:13:43 +0100 Subject: [PATCH 13/80] Moved testfile to /dev/shm to not degrade hardware and speedup testing. --- testing/basic-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/basic-tests.sh b/testing/basic-tests.sh index 4575c7e..3c4ffe7 100755 --- a/testing/basic-tests.sh +++ b/testing/basic-tests.sh @@ -37,7 +37,7 @@ function TEST(){ else echo -n "OK " fi - echo " ${IOR_OUT}/${I} ${IOR_MPIRUN} -o /dev/shm/ior ${@}" + echo " ${IOR_OUT}/${I} ${IOR_MPIRUN} ${@} -o /dev/shm/ior" I=$((${I}+1)) } From c57f8096542cfd433ff8a5c484ef6a7282781912 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 00:20:07 +0100 Subject: [PATCH 14/80] Test scripts can be run from any directory. --- testing/docker/prepare.sh | 5 +++++ testing/docker/run-all-tests.sh | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/testing/docker/prepare.sh b/testing/docker/prepare.sh index c8ed22b..74ecd31 100755 --- a/testing/docker/prepare.sh +++ b/testing/docker/prepare.sh @@ -1,4 +1,9 @@ #!/bin/bash +cd "${0%/*}" +if [[ ! -e run-all-tests.sh ]] ; then + echo "Error, this script must run from the ./testing/docker directory" + exit 1 +fi echo "Checking docker" docker ps diff --git a/testing/docker/run-all-tests.sh b/testing/docker/run-all-tests.sh index 30d2deb..466116f 100755 --- a/testing/docker/run-all-tests.sh +++ b/testing/docker/run-all-tests.sh @@ -1,6 +1,11 @@ #!/bin/bash # This script runs the testscript for all supported docker images +cd "${0%/*}" +if [[ ! -e run-all-tests.sh ]] ; then + echo "Error, this script must run from the ./testing/docker directory" + exit 1 +fi TARGET=../../build-docker mkdir -p $TARGET From fabc74759e93b536ea0c947a18dd4bdf7be64883 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 00:43:24 +0100 Subject: [PATCH 15/80] Minor cleanup. Really use /dev/shm now. --- testing/basic-tests.sh | 5 +-- testing/docker/centos6/run-test.sh | 45 +------------------------- testing/docker/centos7/run-test.sh | 21 ++++-------- testing/docker/ubuntu14.04/run-test.sh | 19 ++++------- testing/docker/ubuntu16.04/run-test.sh | 17 +++------- 5 files changed, 22 insertions(+), 85 deletions(-) mode change 100755 => 120000 testing/docker/centos6/run-test.sh diff --git a/testing/basic-tests.sh b/testing/basic-tests.sh index 3c4ffe7..adcfbc7 100755 --- a/testing/basic-tests.sh +++ b/testing/basic-tests.sh @@ -30,14 +30,15 @@ fi ERRORS=0 # Number of errors detected while running I=0 function TEST(){ - ${IOR_MPIRUN} ${@} ${IOR_EXTRA} 1>${IOR_OUT}/$I 2>&1 + WHAT="${IOR_MPIRUN} ${@} ${IOR_EXTRA} -o /dev/shm/ior" + $WHAT 1>${IOR_OUT}/$I 2>&1 if [[ $? != 0 ]]; then echo -n "ERR" ERRORS=$(($ERRORS + 1)) else echo -n "OK " fi - echo " ${IOR_OUT}/${I} ${IOR_MPIRUN} ${@} -o /dev/shm/ior" + echo " $WHAT" I=$((${I}+1)) } diff --git a/testing/docker/centos6/run-test.sh b/testing/docker/centos6/run-test.sh deleted file mode 100755 index 387997c..0000000 --- a/testing/docker/centos6/run-test.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -BUILD="$1" -if [[ $UID == 0 ]]; then - groupadd -g $3 testuser - useradd -r -u $2 -g testuser testuser - sudo -u testuser $0 $1 - exit $? -fi - -ERROR=0 - -function runTest(){ - P=$PATH - FLAVOR="$1" - MPI_DIR="$2" - export IOR_MPIRUN="$3" - - echo $FLAVOR in $BUILD/$FLAVOR - export PATH=$MPI_DIR/bin:$PATH - mkdir -p $BUILD/$FLAVOR - - pushd $BUILD/$FLAVOR > /dev/null - /data/configure || exit 1 - make || exit 1 - - cd /data/ - export IOR_EXEC=$BUILD/$FLAVOR/src/ior - export IOR_OUT=$BUILD/$FLAVOR/test - ./testing/basic-tests.sh - - ERROR=$(($ERROR + $?)) - popd > /dev/null - PATH=$P -} - - -runTest openmpi /usr/lib64/openmpi/ "mpiexec -n" -export MPI_ARGS="" -runTest mpich /usr/lib64/mpich "mpiexec -n" - -#kill -9 %1 - -exit $ERROR diff --git a/testing/docker/centos6/run-test.sh b/testing/docker/centos6/run-test.sh new file mode 120000 index 0000000..7809c21 --- /dev/null +++ b/testing/docker/centos6/run-test.sh @@ -0,0 +1 @@ +../centos7/run-test.sh \ No newline at end of file diff --git a/testing/docker/centos7/run-test.sh b/testing/docker/centos7/run-test.sh index 088fcb0..6568a0e 100755 --- a/testing/docker/centos7/run-test.sh +++ b/testing/docker/centos7/run-test.sh @@ -2,13 +2,8 @@ BUILD="$1" -if [[ $UID == 0 ]]; then - groupadd -g $3 testuser - useradd -r -u $2 -g testuser testuser - sudo -u testuser $0 $1 - exit $? -fi - +groupadd -g $3 testuser +useradd -r -u $2 -g testuser testuser ERROR=0 function runTest(){ @@ -17,18 +12,16 @@ function runTest(){ MPI_DIR="$2" echo $FLAVOR in $BUILD/$FLAVOR - mkdir -p $BUILD/$FLAVOR + sudo -u testuser mkdir -p $BUILD/$FLAVOR pushd $BUILD/$FLAVOR > /dev/null - + export PATH=$MPI_DIR/bin:$PATH - /data/configure || exit 1 - make || exit 1 + sudo -u testuser PATH=$PATH /data/configure || exit 1 + sudo -u testuser PATH=$PATH make || exit 1 cd /data/ - export IOR_EXEC=$BUILD/$FLAVOR/src/ior - export IOR_OUT=$BUILD/$FLAVOR/test - ./testing/basic-tests.sh + sudo -u testuser PATH=$PATH IOR_EXEC=$BUILD/$FLAVOR/src/ior IOR_OUT=$BUILD/$FLAVOR/test ./testing/basic-tests.sh ERROR=$(($ERROR + $?)) popd > /dev/null diff --git a/testing/docker/ubuntu14.04/run-test.sh b/testing/docker/ubuntu14.04/run-test.sh index b7084c5..3639ae1 100755 --- a/testing/docker/ubuntu14.04/run-test.sh +++ b/testing/docker/ubuntu14.04/run-test.sh @@ -1,12 +1,9 @@ #!/bin/bash BUILD="$1" -if [[ $UID == 0 ]]; then - groupadd -g $3 testuser - useradd -r -u $2 -g testuser testuser - sudo -u testuser $0 $1 - exit $? -fi +groupadd -g $3 testuser +useradd -r -u $2 -g testuser testuser + ERROR=0 function runTest(){ @@ -14,19 +11,17 @@ function runTest(){ MPI_DIR="$2" echo $FLAVOR in $BUILD/$FLAVOR update-alternatives --set mpi $MPI_DIR - mkdir -p $BUILD/$FLAVOR + sudo -u testuser mkdir -p $BUILD/$FLAVOR pushd $BUILD/$FLAVOR > /dev/null - /data/configure || exit 1 - make || exit 1 + sudo -u testuser /data/configure || exit 1 + sudo -u testuser make || exit 1 #define the alias ln -sf $(which mpiexec.$FLAVOR) /usr/bin/mpiexec cd /data/ - export IOR_EXEC=$BUILD/$FLAVOR/src/ior - export IOR_OUT=$BUILD/$FLAVOR/test - ./testing/basic-tests.sh + sudo -u testuser IOR_EXEC=$BUILD/$FLAVOR/src/ior IOR_OUT=$BUILD/$FLAVOR/test ./testing/basic-tests.sh ERROR=$(($ERROR + $?)) popd > /dev/null diff --git a/testing/docker/ubuntu16.04/run-test.sh b/testing/docker/ubuntu16.04/run-test.sh index 950bdaa..03fb99c 100755 --- a/testing/docker/ubuntu16.04/run-test.sh +++ b/testing/docker/ubuntu16.04/run-test.sh @@ -1,15 +1,8 @@ #!/bin/bash BUILD="$1" -if [[ $UID == 0 ]]; then - groupadd -g $3 testuser - useradd -r -u $2 -g testuser testuser - sudo -u testuser $0 $1 - exit $? -fi groupadd -g $3 testuser useradd -r -u $2 -g testuser testuser - ERROR=0 function runTest(){ @@ -18,20 +11,18 @@ function runTest(){ export IOR_MPIRUN="$3" echo $FLAVOR in $BUILD/$FLAVOR update-alternatives --set mpi $MPI_DIR - mkdir -p $BUILD/$FLAVOR + sudo -u testuser mkdir -p $BUILD/$FLAVOR pushd $BUILD/$FLAVOR > /dev/null - /data/configure || exit 1 - make || exit 1 + sudo -u testuser /data/configure || exit 1 + sudo -u testuser make || exit 1 #define the alias ln -sf $(which mpiexec.$FLAVOR) /usr/bin/mpiexec cd /data/ - export IOR_EXEC=$BUILD/$FLAVOR/src/ior - export IOR_OUT=$BUILD/$FLAVOR/test - ./testing/basic-tests.sh + sudo -u testuser IOR_EXEC=$BUILD/$FLAVOR/src/ior IOR_OUT=$BUILD/$FLAVOR/test ./testing/basic-tests.sh ERROR=$(($ERROR + $?)) popd > /dev/null From bcaea2a39f7b15278a0c591d9bb1ee670aee6337 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 08:41:33 +0100 Subject: [PATCH 16/80] Replaced getopt with an alternative version (on some machines optind was broken). --- configure.ac | 2 +- src/Makefile.am | 6 +- src/getopt/COPYING | 674 +++++++++++++++++++++ src/getopt/COPYING.LESSER | 165 ++++++ src/getopt/README | 82 +++ src/getopt/optlist.c | 290 +++++++++ src/getopt/optlist.h | 71 +++ src/getopt/sample.c | 103 ++++ src/iordef.h | 9 +- src/mdtest.c | 18 +- src/parse_options.c | 18 +- src/win/getopt.c | 1188 ------------------------------------- src/win/getopt.h | 192 ------ 13 files changed, 1417 insertions(+), 1401 deletions(-) create mode 100644 src/getopt/COPYING create mode 100644 src/getopt/COPYING.LESSER create mode 100644 src/getopt/README create mode 100644 src/getopt/optlist.c create mode 100644 src/getopt/optlist.h create mode 100644 src/getopt/sample.c delete mode 100755 src/win/getopt.c delete mode 100755 src/win/getopt.h diff --git a/configure.ac b/configure.ac index d1a066a..00e4882 100755 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ AC_CONFIG_HEADER([src/config.h]) AC_CANONICAL_HOST # Automake support -AM_INIT_AUTOMAKE([check-news dist-bzip2 gnu no-define foreign]) +AM_INIT_AUTOMAKE([check-news dist-bzip2 gnu no-define foreign subdir-objects]) AM_MAINTAINER_MODE # Checks for programs diff --git a/src/Makefile.am b/src/Makefile.am index 7d2575b..81ece74 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,19 +3,19 @@ if USE_CAPS bin_PROGRAMS += IOR MDTEST endif -noinst_HEADERS = ior.h utilities.h parse_options.h aiori.h iordef.h +noinst_HEADERS = ior.h utilities.h parse_options.h aiori.h iordef.h getopt/optlist.h extraSOURCES = aiori.c extraLDADD = extraLDFLAGS = extraCPPFLAGS = -ior_SOURCES = ior.c utilities.c parse_options.c +ior_SOURCES = ior.c utilities.c parse_options.c getopt/optlist.c ior_LDFLAGS = ior_LDADD = ior_CPPFLAGS = -mdtest_SOURCES = mdtest.c utilities.c +mdtest_SOURCES = mdtest.c utilities.c getopt/optlist.c mdtest_LDFLAGS = mdtest_LDADD = mdtest_CPPFLAGS = diff --git a/src/getopt/COPYING b/src/getopt/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/src/getopt/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/src/getopt/COPYING.LESSER b/src/getopt/COPYING.LESSER new file mode 100644 index 0000000..fc8a5de --- /dev/null +++ b/src/getopt/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/src/getopt/README b/src/getopt/README new file mode 100644 index 0000000..662c082 --- /dev/null +++ b/src/getopt/README @@ -0,0 +1,82 @@ +DESCRIPTION +----------- +This archive contains the source code and supporting documentation for OptList, +an ANSI C command line option parser library. + +OptList is released under the GNU LGPL version 3.0. + +The latest revision of this program may be found at: +http://michael.dipperstein.com/optlist.html + +FILES +----- +COPYING - Rules for copying and distributing GNU GPL software +COPYING.LESSER - Rules for copying and distributing GNU LGPL software +optlist.c - Source code for the Optlist function and supporting + function. +optlist.h - Header file to be included by code using OptList +Makefile - Makefile for this project (assumes gcc compiler and GNU make) +README - This file +sample.c - A small program demonstrating how to use OptList + +BUILDING +-------- +To build these files with GNU make and gcc: +1. Windows users should define the environment variable OS to be Windows or + Windows_NT. This is often already done. +2. Enter the command "make" from the command line. + +USAGE +----- +The file sample.c demonstrates the usage of OptList. + +SYNOPSIS +typedef struct option_t +{ + char option; + char *argument; + int argIndex; + struct option_t *next; +} option_t; + +option_t *GetOptList(int argc, char *const argv[], char *const options); + + +DESCRIPTION +The GetOptList() function is similar to getopt(). Its most notable differences +are that it returns a linked list to the command line arguments and their +parameters. One call to GetOptList() will return all of the command line +options and their arguments. GetOptList() will not modify argc or argv. + +GetOptList()'s parameters "argc" and "argv" are the argument count and array as +passed to the main() function on program invocation. An element of argv that +starts with "-" is an option element. The character following the "-" is option +an character. + +The parameter "options" is a string containing the legitimate option characters. +If such a character is followed by a colon, the option requires an argument. +(e.g. "a:bc?" a, b ,c, and, ? are all options. a should be followed by an +argument.) + +GetOptList() returns a linked list of type option_t. The "*next" field of the +element at the end of the list will be set to NULL. The "option" field will +contain the option character. A pointer to the following text in the same +argv-element, or the text of the following argv-element will be stored in the +"arguement" field, otherwise the "arguement" field is set to NULL. The index +of the argv-element containing the argument will be stored in the "argIndex". +If there is no argument, the field will contain OL_NOINDEX. + +HISTORY +------- +08/01/07 - Initial release +09/13/14 - Added FindFileName function, because I always use it with GetOptList + Tighter adherence to Michael Barr's "Top 10 Bug-Killing Coding + Standard Rules" (http://www.barrgroup.com/webinars/10rules). + +TODO +---- +- Add support for --option_name + +AUTHOR +------ +Michael Dipperstein (mdipper@alumni.engr.ucsb.edu) diff --git a/src/getopt/optlist.c b/src/getopt/optlist.c new file mode 100644 index 0000000..544855b --- /dev/null +++ b/src/getopt/optlist.c @@ -0,0 +1,290 @@ +/*************************************************************************** +* Command Line Option Parser +* +* File : optlist.c +* Purpose : Provide getopt style command line option parsing +* Author : Michael Dipperstein +* Date : August 1, 2007 +* +**************************************************************************** +* +* OptList: A command line option parsing library +* Copyright (C) 2007, 2014 by +* Michael Dipperstein (mdipper@alumni.engr.ucsb.edu) +* +* This file is part of the OptList library. +* +* OptList is free software; you can redistribute it and/or modify it +* under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation; either version 3 of the License, or (at +* your option) any later version. +* +* OptList is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +* General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +***************************************************************************/ + +/*************************************************************************** +* INCLUDED FILES +***************************************************************************/ +#include "optlist.h" +#include +#include +#include + +/*************************************************************************** +* TYPE DEFINITIONS +***************************************************************************/ + +/*************************************************************************** +* CONSTANTS +***************************************************************************/ + +/*************************************************************************** +* GLOBAL VARIABLES +***************************************************************************/ + +/*************************************************************************** +* PROTOTYPES +***************************************************************************/ +static option_t *MakeOpt( + const char option, char *const argument, const int index); + +static size_t MatchOpt( + const char argument, char *const options); + +/*************************************************************************** +* FUNCTIONS +***************************************************************************/ + +/**************************************************************************** +* Function : GetOptList +* Description: This function is similar to the POSIX function getopt. All +* options and their corresponding arguments are returned in a +* linked list. This function should only be called once per +* an option list and it does not modify argv or argc. +* Parameters : argc - the number of command line arguments (including the +* name of the executable) +* argv - pointer to the open binary file to write encoded +* output +* options - getopt style option list. A NULL terminated +* string of single character options. Follow an +* option with a colon to indicate that it requires +* an argument. +* Effects : Creates a link list of command line options and their +* arguments. +* Returned : option_t type value where the option and arguement fields +* contain the next option symbol and its argument (if any). +* The argument field will be set to NULL if the option is +* specified as having no arguments or no arguments are found. +* The option field will be set to PO_NO_OPT if no more +* options are found. +* +* NOTE: The caller is responsible for freeing up the option list when it +* is no longer needed. +****************************************************************************/ +option_t *GetOptList(const int argc, char *const argv[], char *const options) +{ + int nextArg; + option_t *head, *tail; + size_t optIndex; + size_t argIndex; + + /* start with first argument and nothing found */ + nextArg = 1; + head = NULL; + tail = NULL; + + /* loop through all of the command line arguments */ + while (nextArg < argc) + { + argIndex = 1; + + while ((strlen(argv[nextArg]) > argIndex) && ('-' == argv[nextArg][0])) + { + /* attempt to find a matching option */ + optIndex = MatchOpt(argv[nextArg][argIndex], options); + + if (options[optIndex] == argv[nextArg][argIndex]) + { + /* we found the matching option */ + if (NULL == head) + { + head = MakeOpt(options[optIndex], NULL, OL_NOINDEX); + tail = head; + } + else + { + tail->next = MakeOpt(options[optIndex], NULL, OL_NOINDEX); + tail = tail->next; + } + + if (':' == options[optIndex + 1]) + { + /* the option found should have a text arguement */ + argIndex++; + + if (strlen(argv[nextArg]) > argIndex) + { + /* no space between argument and option */ + tail->argument = &(argv[nextArg][argIndex]); + tail->argIndex = nextArg; + } + else if (nextArg < argc) + { + /* there must be space between the argument option */ + nextArg++; + tail->argument = argv[nextArg]; + tail->argIndex = nextArg; + } + + break; /* done with argv[nextArg] */ + } + } + + argIndex++; + } + + nextArg++; + } + + return head; +} + +/**************************************************************************** +* Function : MakeOpt +* Description: This function uses malloc to allocate space for an option_t +* type structure and initailizes the structure with the +* values passed as a parameter. +* Parameters : option - this option character +* argument - pointer string containg the argument for option. +* Use NULL for no argument +* index - argv[index] contains argument use OL_NOINDEX for +* no argument +* Effects : A new option_t type variable is created on the heap. +* Returned : Pointer to newly created and initialized option_t type +* structure. NULL if space for structure can't be allocated. +****************************************************************************/ +static option_t *MakeOpt( + const char option, char *const argument, const int index) +{ + option_t *opt; + + opt = malloc(sizeof(option_t)); + + if (opt != NULL) + { + opt->option = option; + opt->argument = argument; + opt->argIndex = index; + opt->next = NULL; + } + else + { + perror("Failed to Allocate option_t"); + } + + return opt; +} + +/**************************************************************************** +* Function : FreeOptList +* Description: This function will free all the elements in an option_t +* type linked list starting from the node passed as a +* parameter. +* Parameters : list - head of linked list to be freed +* Effects : All elements of the linked list pointed to by list will +* be freed and list will be set to NULL. +* Returned : None +****************************************************************************/ +void FreeOptList(option_t *list) +{ + option_t *head, *next; + + head = list; + list = NULL; + + while (head != NULL) + { + next = head->next; + free(head); + head = next; + } + + return; +} + +/**************************************************************************** +* Function : MatchOpt +* Description: This function searches for an arguement in an option list. +* It will return the index to the option matching the +* arguement or the index to the NULL if none is found. +* Parameters : arguement - character arguement to be matched to an +* option in the option list +* options - getopt style option list. A NULL terminated +* string of single character options. Follow an +* option with a colon to indicate that it requires +* an argument. +* Effects : None +* Returned : Index of argument in option list. Index of end of string +* if arguement does not appear in the option list. +****************************************************************************/ +static size_t MatchOpt( + const char argument, char *const options) +{ + size_t optIndex = 0; + + /* attempt to find a matching option */ + while ((options[optIndex] != '\0') && + (options[optIndex] != argument)) + { + do + { + optIndex++; + } + while ((options[optIndex] != '\0') && + (':' == options[optIndex])); + } + + return optIndex; +} + +/**************************************************************************** +* Function : FindFileName +* Description: This is function accepts a pointer to the name of a file +* along with path information and returns a pointer to the +* first character that is not part of the path. +* Parameters : fullPath - pointer to an array of characters containing +* a file name and possible path modifiers. +* Effects : None +* Returned : Returns a pointer to the first character after any path +* information. +****************************************************************************/ +char *FindFileName(const char *const fullPath) +{ + int i; + const char *start; /* start of file name */ + const char *tmp; + const char delim[3] = {'\\', '/', ':'}; /* path deliminators */ + + start = fullPath; + + /* find the first character after all file path delimiters */ + for (i = 0; i < 3; i++) + { + tmp = strrchr(start, delim[i]); + + if (tmp != NULL) + { + start = tmp + 1; + } + } + + return (char *)start; +} + diff --git a/src/getopt/optlist.h b/src/getopt/optlist.h new file mode 100644 index 0000000..c8d7af7 --- /dev/null +++ b/src/getopt/optlist.h @@ -0,0 +1,71 @@ +/*************************************************************************** +* Command Line Option Parser +* +* File : optlist.h +* Purpose : Header for getopt style command line option parsing +* Author : Michael Dipperstein +* Date : August 1, 2007 +* +**************************************************************************** +* +* OptList: A command line option parsing library +* Copyright (C) 2007, 20014 by +* Michael Dipperstein (mdipper@alumni.engr.ucsb.edu) +* +* This file is part of the OptList library. +* +* OptList is free software; you can redistribute it and/or modify it +* under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation; either version 3 of the License, or (at +* your option) any later version. +* +* OptList is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +* General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +***************************************************************************/ +#ifndef OPTLIST_H +#define OPTLIST_H + +/*************************************************************************** +* INCLUDED FILES +***************************************************************************/ + +/*************************************************************************** +* MACROS +***************************************************************************/ + +/*************************************************************************** +* CONSTANTS +***************************************************************************/ +#define OL_NOINDEX -1 /* this option has no arguement */ + +/*************************************************************************** +* TYPE DEFINITIONS +***************************************************************************/ +typedef struct option_t +{ + char option; /* the current character option character */ + char *argument; /* pointer to arguments for this option */ + int argIndex; /* index into argv[] containing the argument */ + struct option_t *next; /* the next option in the linked list */ +} option_t; + +/*************************************************************************** +* PROTOTYPES +***************************************************************************/ + +/* returns a linked list of options and arguments similar to getopt() */ +option_t *GetOptList(int argc, char *const argv[], char *const options); + +/* frees the linked list of option_t returned by GetOptList */ +void FreeOptList(option_t *list); + +/* return a pointer to file name in a full path. useful for argv[0] */ +char *FindFileName(const char *const fullPath); + +#endif /* ndef OPTLIST_H */ diff --git a/src/getopt/sample.c b/src/getopt/sample.c new file mode 100644 index 0000000..d589515 --- /dev/null +++ b/src/getopt/sample.c @@ -0,0 +1,103 @@ +/*************************************************************************** +* OptList Usage Sample +* +* File : sample.c +* Purpose : Demonstrates usage of optlist library. +* Author : Michael Dipperstein +* Date : July 23, 2004 +* +**************************************************************************** +* +* Sample: A optlist library sample usage program +* Copyright (C) 2007, 2014 by +* Michael Dipperstein (mdipper@alumni.engr.ucsb.edu) +* +* This file is part of the optlist library. +* +* The optlist library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 3 of the +* License, or (at your option) any later version. +* +* The optlist library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +* General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +***************************************************************************/ + +/*************************************************************************** +* INCLUDED FILES +***************************************************************************/ +#include +#include +#include "optlist.h" + +/*************************************************************************** +* PROTOTYPES +***************************************************************************/ + +/*************************************************************************** +* FUNCTIONS +***************************************************************************/ + +/**************************************************************************** +* Function : main +* Description: This is the main function for this program, it calls +* optlist to parse the command line input displays the +* results of the parsing. +* Parameters : argc - number of parameters +* argv - parameter list +* Effects : parses command line parameters +* Returned : EXIT_SUCCESS for success, otherwise EXIT_FAILURE. +****************************************************************************/ +int main(int argc, char *argv[]) +{ + option_t *optList, *thisOpt; + + /* get list of command line options and their arguments */ + optList = NULL; + optList = GetOptList(argc, argv, "a:bcd:ef?"); + + /* display results of parsing */ + while (optList != NULL) + { + thisOpt = optList; + optList = optList->next; + + if ('?' == thisOpt->option) + { + printf("Usage: %s \n\n", FindFileName(argv[0])); + printf("options:\n"); + printf(" -a : option excepting argument.\n"); + printf(" -b : option without arguments.\n"); + printf(" -c : option without arguments.\n"); + printf(" -d : option excepting argument.\n"); + printf(" -e : option without arguments.\n"); + printf(" -f : option without arguments.\n"); + printf(" -? : print out command line options.\n\n"); + + FreeOptList(thisOpt); /* free the rest of the list */ + return EXIT_SUCCESS; + } + + printf("found option %c\n", thisOpt->option); + + if (thisOpt->argument != NULL) + { + printf("\tfound argument %s", thisOpt->argument); + printf(" at index %d\n", thisOpt->argIndex); + } + else + { + printf("\tno argument for this option\n"); + } + + free(thisOpt); /* done with this item, free it */ + } + + return EXIT_SUCCESS; +} diff --git a/src/iordef.h b/src/iordef.h index 6d2ff3e..8a76d3d 100755 --- a/src/iordef.h +++ b/src/iordef.h @@ -31,7 +31,6 @@ # include # include # include -# include "win/getopt.h" # define MAXPATHLEN 1024 # define F_OK 00 @@ -41,7 +40,7 @@ # define lseek _lseeki64 # define fsync _commit -# define mkdir(dir, mode) _mkdir(dir) +# define mkdir(dir, mode) _mkdir(dir) # define strcasecmp _stricmp # define strncasecmp _strnicmp # define srandom srand @@ -102,8 +101,8 @@ extern int verbose; /* verbose output */ #define VERBOSE_4 4 #define VERBOSE_5 5 -#define MAX_STR 1024 /* max string length */ -#define MAX_HINTS 16 /* max number of hints */ +#define MAX_STR 1024 /* max string length */ +#define MAX_HINTS 16 /* max number of hints */ #define MAX_RETRY 10000 /* max retries for POSIX xfer */ #ifndef PATH_MAX #define PATH_MAX 4096 @@ -211,7 +210,7 @@ struct utsname { char nodename[257]; char release [16]; char version [16]; - char machine [16]; + char machine [16]; }; extern int uname(struct utsname *name); diff --git a/src/mdtest.c b/src/mdtest.c index aadbf78..52cfbb2 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -38,6 +38,8 @@ #include #include +#include "getopt/optlist.h" + #if HAVE_SYS_PARAM_H #include #endif @@ -1802,7 +1804,7 @@ void create_remove_directory_tree(int create, } int main(int argc, char **argv) { - int i, j, k, c; + int i, j, k; int nodeCount; MPI_Group worldgroup, testgroup; struct { @@ -1852,13 +1854,15 @@ int main(int argc, char **argv) { } /* Parse command line options */ - while (1) { - c = getopt(argc, argv, "a:b:BcCd:De:Ef:Fhi:I:l:Ln:N:p:rR::s:StTuvV:w:yz:"); - if (c == -1) { - break; - } + option_t *optList, *thisOpt; + optList = GetOptList(argc, argv, "a:b:BcCd:De:Ef:Fhi:I:l:Ln:N:p:rR::s:StTuvV:w:W:yz:Z"); - switch (c) { + while (optList != NULL) { + thisOpt = optList; + optarg = thisOpt->argument; + optList = optList->next; + + switch (thisOpt->option) { case 'a': backend_name = optarg; break; case 'b': diff --git a/src/parse_options.c b/src/parse_options.c index cfa388a..cd061ed 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -21,6 +21,8 @@ #include #include +#include "getopt/optlist.h" + #include "ior.h" #include "aiori.h" #include "parse_options.h" @@ -317,7 +319,7 @@ void DecodeDirective(char *line, IOR_param_t *params) ERR("beegfsNumTargets must be >= 1"); } else if (strcasecmp(option, "beegfsChunkSize") == 0) { #ifndef HAVE_BEEGFS_BEEGFS_H - ERR("ior was not compiled with BeeGFS support"); + ERR("ior was not compiled with BeeGFS support"); #endif params->beegfs_chunkSize = StringToBytes(value); if (!ISPOWEROFTWO(params->beegfs_chunkSize) || params->beegfs_chunkSize < (1<<16)) @@ -453,9 +455,9 @@ IOR_test_t *ReadConfigScript(char *scriptName) */ IOR_test_t *ParseCommandLine(int argc, char **argv) { - static const char *opts = + static char * const opts = "a:A:b:BcCd:D:eEf:FgG:hHi:Ij:J:kKl:mM:nN:o:O:pPqQ:rRs:St:T:uU:vVwWxX:YzZ"; - int c, i; + int i; static IOR_test_t *tests = NULL; /* suppress getopt() error message when a character is unrecognized */ @@ -466,8 +468,14 @@ IOR_test_t *ParseCommandLine(int argc, char **argv) initialTestParams.writeFile = initialTestParams.readFile = FALSE; initialTestParams.checkWrite = initialTestParams.checkRead = FALSE; - while ((c = getopt(argc, argv, opts)) != -1) { - switch (c) { + option_t *optList, *thisOpt; + optList = GetOptList(argc, argv, opts); + + while (optList != NULL) { + thisOpt = optList; + optarg = thisOpt->argument; + optList = optList->next; + switch (thisOpt->option) { case 'a': strcpy(initialTestParams.api, optarg); break; diff --git a/src/win/getopt.c b/src/win/getopt.c deleted file mode 100755 index 77ff545..0000000 --- a/src/win/getopt.c +++ /dev/null @@ -1,1188 +0,0 @@ -/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- - * vim:expandtab:shiftwidth=8:tabstop=8: - */ -/* Getopt for GNU. - NOTE: getopt is now part of the C library, so if you don't know what - "Keep this file name-space clean" means, talk to drepper@gnu.org - before changing it! - Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001 - Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ - -/* This tells Alpha OSF/1 not to define a getopt prototype in . - Ditto for AIX 3.2 and . */ - -#define _CRT_SECURE_NO_WARNINGS - -#ifndef _NO_PROTO -# define _NO_PROTO -#endif - -#ifdef HAVE_CONFIG_H -# include -#endif - -#if !defined __STDC__ || !__STDC__ -/* This is a separate conditional since some stdc systems - reject `defined (const)'. */ -# ifndef const -# define const -# endif -#endif - -#include -#include - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#define GETOPT_INTERFACE_VERSION 2 -#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 -# include -# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION -# define ELIDE_CODE -# endif -#endif - -#ifndef ELIDE_CODE - - -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -/* Don't include stdlib.h for non-GNU C libraries because some of them - contain conflicting prototypes for getopt. */ -# include -# include -#endif /* GNU C library. */ - -#ifdef VMS -# include -# if HAVE_STRING_H - 0 -# include -# endif -#endif - -#ifndef _ -/* This is for other GNU distributions with internationalized messages. */ -# if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC -# include -# ifndef _ -# define _(msgid) gettext (msgid) -# endif -# else -# define _(msgid) (msgid) -# endif -# if defined _LIBC && defined USE_IN_LIBIO -# include -# endif -#endif - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - Setting the environment variable POSIXLY_CORRECT disables permutation. - Then the behavior is completely standard. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ - -#include "getopt.h" - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -char *optarg; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -/* 1003.2 says this must be 1 before any call. */ -int optind = 1; - -/* Formerly, initialization of getopt depended on optind==0, which - causes problems with re-calling getopt as programs generally don't - know that. */ - -int __getopt_initialized; - -/* The next char to be scanned in the option-element - in which the last option character we returned was found. - This allows us to pick up the scan where we left off. - - If this is zero, or a null string, it means resume the scan - by advancing to the next ARGV-element. */ - -static char *nextchar; - -/* Callers store zero here to inhibit the error message - for unrecognized options. */ - -int opterr = 1; - -/* Set to an option character which was unrecognized. - This must be initialized on some systems to avoid linking in the - system's own getopt implementation. */ - -int optopt = '?'; - -/* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, - the default is REQUIRE_ORDER if the environment variable - POSIXLY_CORRECT is defined, PERMUTE otherwise. - - REQUIRE_ORDER means don't recognize them as options; - stop option processing when the first non-option is seen. - This is what Unix does. - This mode of operation is selected by either setting the environment - variable POSIXLY_CORRECT, or using `+' as the first character - of the list of option characters. - - PERMUTE is the default. We permute the contents of ARGV as we scan, - so that eventually all the non-options are at the end. This allows options - to be given in any order, even with programs that were not written to - expect this. - - RETURN_IN_ORDER is an option available to programs that were written - to expect options and other ARGV-elements in any order and that care about - the ordering of the two. We describe each non-option ARGV-element - as if it were the argument of an option with character code 1. - Using `-' as the first character of the list of option characters - selects this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return -1 with `optind' != ARGC. */ - -static enum -{ - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER -} ordering; - -/* Value of POSIXLY_CORRECT environment variable. */ -static char *posixly_correct; - -#ifdef __GNU_LIBRARY__ -/* We want to avoid inclusion of string.h with non-GNU libraries - because there are many ways it can cause trouble. - On some systems, it contains special magic macros that don't work - in GCC. */ -# include -# define my_index strchr -#else - -# if HAVE_STRING_H || WIN32 /* Pete Wilson mod 7/28/02 */ -# include -# else -# include -# endif - -/* Avoid depending on library functions or files - whose names are inconsistent. */ - -#ifndef getenv -//extern char *getenv (); -#endif - -static char * -my_index (str, chr) - const char *str; - int chr; -{ - while (*str) - { - if (*str == chr) - return (char *) str; - str++; - } - return 0; -} - -/* If using GCC, we can safely declare strlen this way. - If not using GCC, it is ok not to declare it. */ -#ifdef __GNUC__ -/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. - That was relevant to code that was here before. */ -# if (!defined __STDC__ || !__STDC__) && !defined strlen -/* gcc with -traditional declares the built-in strlen to return int, - and has done so at least since version 2.4.5. -- rms. */ -extern int strlen (const char *); -# endif /* not __STDC__ */ -#endif /* __GNUC__ */ - -#endif /* not __GNU_LIBRARY__ */ - -/* Handle permutation of arguments. */ - -/* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first of them; - `last_nonopt' is the index after the last of them. */ - -static int first_nonopt; -static int last_nonopt; - -#ifdef _LIBC -/* Stored original parameters. - XXX This is no good solution. We should rather copy the args so - that we can compare them later. But we must not use malloc(3). */ -extern int __libc_argc; -extern char **__libc_argv; - -/* Bash 2.0 gives us an environment variable containing flags - indicating ARGV elements that should not be considered arguments. */ - -# ifdef USE_NONOPTION_FLAGS -/* Defined in getopt_init.c */ -extern char *__getopt_nonoption_flags; - -static int nonoption_flags_max_len; -static int nonoption_flags_len; -# endif - -# ifdef USE_NONOPTION_FLAGS -# define SWAP_FLAGS(ch1, ch2) \ - if (nonoption_flags_len > 0) \ - { \ - char __tmp = __getopt_nonoption_flags[ch1]; \ - __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ - __getopt_nonoption_flags[ch2] = __tmp; \ - } -# else -# define SWAP_FLAGS(ch1, ch2) -# endif -#else /* !_LIBC */ -# define SWAP_FLAGS(ch1, ch2) -#endif /* _LIBC */ - -/* Exchange two adjacent subsequences of ARGV. - One subsequence is elements [first_nonopt,last_nonopt) - which contains all the non-options that have been skipped so far. - The other is elements [last_nonopt,optind), which contains all - the options processed since those non-options were skipped. - - `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ - -#if defined __STDC__ && __STDC__ -static void exchange (char **); -#endif - -static void -exchange (argv) - char **argv; -{ - int bottom = first_nonopt; - int middle = last_nonopt; - int top = optind; - char *tem; - - /* Exchange the shorter segment with the far end of the longer segment. - That puts the shorter segment into the right place. - It leaves the longer segment in the right place overall, - but it consists of two parts that need to be swapped next. */ - -#if defined _LIBC && defined USE_NONOPTION_FLAGS - /* First make sure the handling of the `__getopt_nonoption_flags' - string can work normally. Our top argument must be in the range - of the string. */ - if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) - { - /* We must extend the array. The user plays games with us and - presents new arguments. */ - char *new_str = malloc (top + 1); - if (new_str == NULL) - nonoption_flags_len = nonoption_flags_max_len = 0; - else - { - memset (__mempcpy (new_str, __getopt_nonoption_flags, - nonoption_flags_max_len), - '\0', top + 1 - nonoption_flags_max_len); - nonoption_flags_max_len = top + 1; - __getopt_nonoption_flags = new_str; - } - } -#endif - - while (top > middle && middle > bottom) - { - if (top - middle > middle - bottom) - { - /* Bottom segment is the short one. */ - int len = middle - bottom; - register int i; - - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); - } - /* Exclude the moved bottom segment from further swapping. */ - top -= len; - } - else - { - /* Top segment is the short one. */ - int len = top - middle; - register int i; - - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - SWAP_FLAGS (bottom + i, middle + i); - } - /* Exclude the moved top segment from further swapping. */ - bottom += len; - } - } - - /* Update records for the slots the non-options now occupy. */ - - first_nonopt += (optind - last_nonopt); - last_nonopt = optind; -} - -/* Initialize the internal data when the first call is made. */ - -#if defined __STDC__ && __STDC__ -static const char *_getopt_initialize (int, char *const *, const char *); -#endif -static const char * -_getopt_initialize (argc, argv, optstring) - int argc; - char *const *argv; - const char *optstring; -{ - /* Start processing options with ARGV-element 1 (since ARGV-element 0 - is the program name); the sequence of previously skipped - non-option ARGV-elements is empty. */ - - first_nonopt = last_nonopt = optind; - - nextchar = NULL; - - posixly_correct = getenv ("POSIXLY_CORRECT"); - - /* Determine how to handle the ordering of options and nonoptions. */ - - if (optstring[0] == '-') - { - ordering = RETURN_IN_ORDER; - ++optstring; - } - else if (optstring[0] == '+') - { - ordering = REQUIRE_ORDER; - ++optstring; - } - else if (posixly_correct != NULL) - ordering = REQUIRE_ORDER; - else - ordering = PERMUTE; - -#if defined _LIBC && defined USE_NONOPTION_FLAGS - if (posixly_correct == NULL - && argc == __libc_argc && argv == __libc_argv) - { - if (nonoption_flags_max_len == 0) - { - if (__getopt_nonoption_flags == NULL - || __getopt_nonoption_flags[0] == '\0') - nonoption_flags_max_len = -1; - else - { - const char *orig_str = __getopt_nonoption_flags; - int len = nonoption_flags_max_len = strlen (orig_str); - if (nonoption_flags_max_len < argc) - nonoption_flags_max_len = argc; - __getopt_nonoption_flags = - (char *) malloc (nonoption_flags_max_len); - if (__getopt_nonoption_flags == NULL) - nonoption_flags_max_len = -1; - else - memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), - '\0', nonoption_flags_max_len - len); - } - } - nonoption_flags_len = nonoption_flags_max_len; - } - else - nonoption_flags_len = 0; -#endif - - return optstring; -} - -/* Scan elements of ARGV (whose length is ARGC) for option characters - given in OPTSTRING. - - If an element of ARGV starts with '-', and is not exactly "-" or "--", - then it is an option element. The characters of this element - (aside from the initial '-') are option characters. If `getopt' - is called repeatedly, it returns successively each of the option characters - from each of the option elements. - - If `getopt' finds another option character, it returns that character, - updating `optind' and `nextchar' so that the next call to `getopt' can - resume the scan with the following option character or ARGV-element. - - If there are no more option characters, `getopt' returns -1. - Then `optind' is the index in ARGV of the first ARGV-element - that is not an option. (The ARGV-elements have been permuted - so that those that are not options now come last.) - - OPTSTRING is a string containing the legitimate option characters. - If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `opterr' to - zero, the error message is suppressed but we still return '?'. - - If a char in OPTSTRING is followed by a colon, that means it wants an arg, - so the following text in the same ARGV-element, or the text of the following - ARGV-element, is returned in `optarg'. Two colons mean an option that - wants an optional arg; if there is text in the current ARGV-element, - it is returned in `optarg', otherwise `optarg' is set to zero. - - If OPTSTRING starts with `-' or `+', it requests different methods of - handling the non-option ARGV-elements. - See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - - Long-named options begin with `--' instead of `-'. - Their names may be abbreviated as long as the abbreviation is unique - or is an exact match for some defined option. If they have an - argument, it follows the option name in the same ARGV-element, separated - from the option name by a `=', or else the in next ARGV-element. - When `getopt' finds a long-named option, it returns 0 if that option's - `flag' field is nonzero, the value of the option's `val' field - if the `flag' field is zero. - - The elements of ARGV aren't really const, because we permute them. - But we pretend they're const in the prototype to be compatible - with other systems. - - LONGOPTS is a vector of `struct option' terminated by an - element containing a name which is zero. - - LONGIND returns the index in LONGOPT of the long-named option found. - It is only valid when a long-named option has been found by the most - recent call. - - If LONG_ONLY is nonzero, '-' as well as '--' can introduce - long-named options. */ - -int -_getopt_internal (argc, argv, optstring, longopts, longind, long_only) - int argc; - char *const *argv; - const char *optstring; - const struct option *longopts; - int *longind; - int long_only; -{ - int print_errors = opterr; - if (optstring[0] == ':') - print_errors = 0; - - if (argc < 1) - return -1; - - optarg = NULL; - - if (optind == 0 || !__getopt_initialized) - { - if (optind == 0) - optind = 1; /* Don't scan ARGV[0], the program name. */ - optstring = _getopt_initialize (argc, argv, optstring); - __getopt_initialized = 1; - } - - /* Test whether ARGV[optind] points to a non-option argument. - Either it does not have option syntax, or there is an environment flag - from the shell indicating it is not an option. The later information - is only used when the used in the GNU libc. */ -#if defined _LIBC && defined USE_NONOPTION_FLAGS -# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ - || (optind < nonoption_flags_len \ - && __getopt_nonoption_flags[optind] == '1')) -#else -# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') -#endif - - if (nextchar == NULL || *nextchar == '\0') - { - /* Advance to the next ARGV-element. */ - - /* Give FIRST_NONOPT and LAST_NONOPT rational values if OPTIND has been - moved back by the user (who may also have changed the arguments). */ - if (last_nonopt > optind) - last_nonopt = optind; - if (first_nonopt > optind) - first_nonopt = optind; - - if (ordering == PERMUTE) - { - /* If we have just processed some options following some non-options, - exchange them so that the options come first. */ - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange ((char **) argv); - else if (last_nonopt != optind) - first_nonopt = optind; - - /* Skip any additional non-options - and extend the range of non-options previously skipped. */ - - while (optind < argc && NONOPTION_P) - optind++; - last_nonopt = optind; - } - - /* The special ARGV-element `--' means premature end of options. - Skip it like a null option, - then exchange with previous non-options as if it were an option, - then skip everything else like a non-option. */ - - if (optind != argc && !strcmp (argv[optind], "--")) - { - optind++; - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange ((char **) argv); - else if (first_nonopt == last_nonopt) - first_nonopt = optind; - last_nonopt = argc; - - optind = argc; - } - - /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. */ - - if (optind == argc) - { - /* Set the next-arg-index to point at the non-options - that we previously skipped, so the caller will digest them. */ - if (first_nonopt != last_nonopt) - optind = first_nonopt; - return -1; - } - - /* If we have come to a non-option and did not permute it, - either stop the scan or describe it to the caller and pass it by. */ - - if (NONOPTION_P) - { - if (ordering == REQUIRE_ORDER) - return -1; - optarg = argv[optind++]; - return 1; - } - - /* We have found another option-ARGV-element. - Skip the initial punctuation. */ - - nextchar = (argv[optind] + 1 - + (longopts != NULL && argv[optind][1] == '-')); - } - - /* Decode the current option-ARGV-element. */ - - /* Check whether the ARGV-element is a long option. - - If long_only and the ARGV-element has the form "-f", where f is - a valid short option, don't consider it an abbreviated form of - a long option that starts with f. Otherwise there would be no - way to give the -f short option. - - On the other hand, if there's a long option "fubar" and - the ARGV-element is "-fu", do consider that an abbreviation of - the long option, just like "--fu", and not "-f" with arg "u". - - This distinction seems to be the most useful approach. */ - - if (longopts != NULL - && (argv[optind][1] == '-' - || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) - { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = -1; - int option_index; - - for (nameend = nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, nextchar, nameend - nextchar)) - { - if ((unsigned int) (nameend - nextchar) - == (unsigned int) strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else if (long_only - || pfound->has_arg != p->has_arg - || pfound->flag != p->flag - || pfound->val != p->val) - /* Second or later nonexact match found. */ - ambig = 1; - } - - if (ambig && !exact) - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - __asprintf (&buf, _("%s: option `%s' is ambiguous\n"), - argv[0], argv[optind]); - - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); - - free (buf); -#else - fprintf (stderr, _("%s: option `%s' is ambiguous\n"), - argv[0], argv[optind]); -#endif - } - nextchar += strlen (nextchar); - optind++; - optopt = 0; - return '?'; - } - - if (pfound != NULL) - { - option_index = indfound; - optind++; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; -#endif - - if (argv[optind - 1][1] == '-') - { - /* --option */ -#if defined _LIBC && defined USE_IN_LIBIO - __asprintf (&buf, _("\ -%s: option `--%s' doesn't allow an argument\n"), - argv[0], pfound->name); -#else - fprintf (stderr, _("\ -%s: option `--%s' doesn't allow an argument\n"), - argv[0], pfound->name); -#endif - } - else - { - /* +option or -option */ -#if defined _LIBC && defined USE_IN_LIBIO - __asprintf (&buf, _("\ -%s: option `%c%s' doesn't allow an argument\n"), - argv[0], argv[optind - 1][0], - pfound->name); -#else - fprintf (stderr, _("\ -%s: option `%c%s' doesn't allow an argument\n"), - argv[0], argv[optind - 1][0], pfound->name); -#endif - } - -#if defined _LIBC && defined USE_IN_LIBIO - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); - - free (buf); -#endif - } - - nextchar += strlen (nextchar); - - optopt = pfound->val; - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - __asprintf (&buf, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); - - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); - - free (buf); -#else - fprintf (stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); -#endif - } - nextchar += strlen (nextchar); - optopt = pfound->val; - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen (nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - - /* Can't find it as a long option. If this is not getopt_long_only, - or the option starts with '--' or is not a valid short - option, then it's an error. - Otherwise interpret it as a short option. */ - if (!long_only || argv[optind][1] == '-' - || my_index (optstring, *nextchar) == NULL) - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; -#endif - - if (argv[optind][1] == '-') - { - /* --option */ -#if defined _LIBC && defined USE_IN_LIBIO - __asprintf (&buf, _("%s: unrecognized option `--%s'\n"), - argv[0], nextchar); -#else - fprintf (stderr, _("%s: unrecognized option `--%s'\n"), - argv[0], nextchar); -#endif - } - else - { - /* +option or -option */ -#if defined _LIBC && defined USE_IN_LIBIO - __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"), - argv[0], argv[optind][0], nextchar); -#else - fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), - argv[0], argv[optind][0], nextchar); -#endif - } - -#if defined _LIBC && defined USE_IN_LIBIO - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); - - free (buf); -#endif - } - nextchar = (char *) ""; - optind++; - optopt = 0; - return '?'; - } - } - - /* Look at and handle the next short option-character. */ - - { - char c = *nextchar++; - char *temp = my_index (optstring, c); - - /* Increment `optind' when we start to process its last character. */ - if (*nextchar == '\0') - ++optind; - - if (temp == NULL || c == ':') - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; -#endif - - if (posixly_correct) - { - /* 1003.2 specifies the format of this message. */ -#if defined _LIBC && defined USE_IN_LIBIO - __asprintf (&buf, _("%s: illegal option -- %c\n"), - argv[0], c); -#else - fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); -#endif - } - else - { -#if defined _LIBC && defined USE_IN_LIBIO - __asprintf (&buf, _("%s: invalid option -- %c\n"), - argv[0], c); -#else - fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); -#endif - } - -#if defined _LIBC && defined USE_IN_LIBIO - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); - - free (buf); -#endif - } - optopt = c; - return '?'; - } - /* Convenience. Treat POSIX -W foo same as long option --foo */ - if (temp[0] == 'W' && temp[1] == ';') - { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = 0; - int option_index; - - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - if (print_errors) - { - /* 1003.2 specifies the format of this message. */ -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - __asprintf (&buf, _("%s: option requires an argument -- %c\n"), - argv[0], c); - - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); - - free (buf); -#else - fprintf (stderr, _("%s: option requires an argument -- %c\n"), - argv[0], c); -#endif - } - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - return c; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - - /* optarg is now the argument, see if it's in the - table of longopts. */ - - for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, nextchar, nameend - nextchar)) - { - if ((unsigned int) (nameend - nextchar) == strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second or later nonexact match found. */ - ambig = 1; - } - if (ambig && !exact) - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - __asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"), - argv[0], argv[optind]); - - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); - - free (buf); -#else - fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), - argv[0], argv[optind]); -#endif - } - nextchar += strlen (nextchar); - optind++; - return '?'; - } - if (pfound != NULL) - { - option_index = indfound; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - __asprintf (&buf, _("\ -%s: option `-W %s' doesn't allow an argument\n"), - argv[0], pfound->name); - - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); - - free (buf); -#else - fprintf (stderr, _("\ -%s: option `-W %s' doesn't allow an argument\n"), - argv[0], pfound->name); -#endif - } - - nextchar += strlen (nextchar); - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - __asprintf (&buf, _("\ -%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); - - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); - - free (buf); -#else - fprintf (stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); -#endif - } - nextchar += strlen (nextchar); - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen (nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - nextchar = NULL; - return 'W'; /* Let the application handle it. */ - } - if (temp[1] == ':') - { - if (temp[2] == ':') - { - /* This is an option that accepts an argument optionally. */ - if (*nextchar != '\0') - { - optarg = nextchar; - optind++; - } - else - optarg = NULL; - nextchar = NULL; - } - else - { - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - if (print_errors) - { - /* 1003.2 specifies the format of this message. */ -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - __asprintf (&buf, - _("%s: option requires an argument -- %c\n"), - argv[0], c); - - if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s", buf); - else - fputs (buf, stderr); - - free (buf); -#else - fprintf (stderr, - _("%s: option requires an argument -- %c\n"), - argv[0], c); -#endif - } - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - nextchar = NULL; - } - } - return c; - } -} - -int -getopt (argc, argv, optstring) - int argc; - char *const *argv; - const char *optstring; -{ - return _getopt_internal (argc, argv, optstring, - (const struct option *) 0, - (int *) 0, - 0); -} - -#endif /* Not ELIDE_CODE. */ - diff --git a/src/win/getopt.h b/src/win/getopt.h deleted file mode 100755 index d3990e1..0000000 --- a/src/win/getopt.h +++ /dev/null @@ -1,192 +0,0 @@ -/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- - * vim:expandtab:shiftwidth=8:tabstop=8: - */ -/* getopt.h */ -/* Declarations for getopt. - Copyright (C) 1989-1994, 1996-1999, 2001 Free Software - Foundation, Inc. This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute - it and/or modify it under the terms of the GNU Lesser - General Public License as published by the Free Software - Foundation; either version 2.1 of the License, or - (at your option) any later version. - - The GNU C Library is distributed in the hope that it will - be useful, but WITHOUT ANY WARRANTY; without even the - implied warranty of MERCHANTABILITY or FITNESS FOR A - PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with the GNU C Library; if not, write - to the Free Software Foundation, Inc., 59 Temple Place, - Suite 330, Boston, MA 02111-1307 USA. */ - - - - - -#ifndef _GETOPT_H - -#ifndef __need_getopt -# define _GETOPT_H 1 -#endif - -/* If __GNU_LIBRARY__ is not already defined, either we are being used - standalone, or this is the first header included in the source file. - If we are being used with glibc, we need to include , but - that does not exist if we are standalone. So: if __GNU_LIBRARY__ is - not defined, include , which will pull in for us - if it's from glibc. (Why ctype.h? It's guaranteed to exist and it - doesn't flood the namespace with stuff the way some other headers do.) */ -#if !defined __GNU_LIBRARY__ -# include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -extern char *optarg; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -extern int optind; - -/* Callers store zero here to inhibit the error message `getopt' prints - for unrecognized options. */ - -extern int opterr; - -/* Set to an option character which was unrecognized. */ - -extern int optopt; - -#ifndef __need_getopt -/* Describe the long-named options requested by the application. - The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector - of `struct option' terminated by an element containing a name which is - zero. - - The field `has_arg' is: - no_argument (or 0) if the option does not take an argument, - required_argument (or 1) if the option requires an argument, - optional_argument (or 2) if the option takes an optional argument. - - If the field `flag' is not NULL, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -struct option -{ -# if (defined __STDC__ && __STDC__) || defined __cplusplus - const char *name; -# else - char *name; -# endif - /* has_arg can't be an enum because some compilers complain about - type mismatches in all the code that assumes it is an int. */ - int has_arg; - int *flag; - int val; -}; - -/* Names for the values of the `has_arg' field of `struct option'. */ - -# define no_argument 0 -# define required_argument 1 -# define optional_argument 2 -#endif /* need getopt */ - - -/* Get definitions and prototypes for functions to process the - arguments in ARGV (ARGC of them, minus the program name) for - options given in OPTS. - - Return the option character from OPTS just read. Return -1 when - there are no more options. For unrecognized options, or options - missing arguments, `optopt' is set to the option letter, and '?' is - returned. - - The OPTS string is a list of characters which are recognized option - letters, optionally followed by colons, specifying that that letter - takes an argument, to be placed in `optarg'. - - If a letter in OPTS is followed by two colons, its argument is - optional. This behavior is specific to the GNU `getopt'. - - The argument `--' causes premature termination of argument - scanning, explicitly telling `getopt' that there are no more - options. - - If OPTS begins with `--', then non-option arguments are treated as - arguments to the option '\0'. This behavior is specific to the GNU - `getopt'. */ - -#if (defined __STDC__ && __STDC__) || defined __cplusplus -# ifdef __cplusplus // __GNU_LIBRARY__ -/* Many other libraries have conflicting prototypes for getopt, with - differences in the consts, in stdlib.h. To avoid compilation - errors, only prototype getopt for the GNU C library. */ -extern int getopt (int ___argc, char *const *___argv, const char *__shortopts); -# else /* not __GNU_LIBRARY__ */ -extern int getopt (); -# endif /* __GNU_LIBRARY__ */ - -# ifndef __need_getopt -extern int getopt_long (int ___argc, char *const *___argv, - const char *__shortopts, - const struct option *__longopts, int *__longind); -extern int getopt_long_only (int ___argc, char *const *___argv, - const char *__shortopts, - const struct option *__longopts, int *__longind); - -/* Internal only. Users should not call this directly. */ -extern int _getopt_internal (int ___argc, char *const *___argv, - const char *__shortopts, - const struct option *__longopts, int *__longind, - int __long_only); -# endif -#else /* not __STDC__ */ -extern int getopt (); -# ifndef __need_getopt -extern int getopt_long (); -extern int getopt_long_only (); - -extern int _getopt_internal (); -# endif -#endif /* __STDC__ */ - -#ifdef __cplusplus -} -#endif - -/* Make sure we later can get all the definitions and declarations. */ -#undef __need_getopt - -#endif /* getopt.h */ - From a6bfa0f94bb6c51e690375218f9c8481afccd4f9 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 10:29:27 +0100 Subject: [PATCH 17/80] MDTest library option. Moved core functionality / variables used by both implementations to utilities --- src/Makefile.am | 2 +- src/ior.c | 49 +- src/ior.h | 24 +- src/mdtest-main.c | 10 + src/mdtest.c | 1343 +++++++++++++----------- src/mdtest.h | 36 + src/parse_options.c | 55 +- src/utilities.c | 97 +- src/utilities.h | 14 + testing/basic-tests.sh | 66 +- testing/docker/centos7/run-test.sh | 2 +- testing/docker/ubuntu14.04/run-test.sh | 2 +- testing/docker/ubuntu16.04/run-test.sh | 2 +- 13 files changed, 989 insertions(+), 713 deletions(-) create mode 100644 src/mdtest-main.c create mode 100644 src/mdtest.h diff --git a/src/Makefile.am b/src/Makefile.am index 81ece74..20a1c3f 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,7 +15,7 @@ ior_LDFLAGS = ior_LDADD = ior_CPPFLAGS = -mdtest_SOURCES = mdtest.c utilities.c getopt/optlist.c +mdtest_SOURCES = mdtest-main.c mdtest.c utilities.c getopt/optlist.c mdtest_LDFLAGS = mdtest_LDADD = mdtest_CPPFLAGS = diff --git a/src/ior.c b/src/ior.c index b92b40d..769abfd 100755 --- a/src/ior.c +++ b/src/ior.c @@ -36,19 +36,9 @@ #include "parse_options.h" -/* globals used by other files, also defined "extern" in ior.h */ -int numTasksWorld = 0; -int rank = 0; -int rankOffset = 0; -int tasksPerNode = 0; /* tasks per node */ -int verbose = VERBOSE_0; /* verbose output */ -MPI_Comm testComm; - /* file scope globals */ extern char **environ; int totalErrorCount = 0; -double wall_clock_delta = 0; -double wall_clock_deviation; const ior_aiori_t *backend; @@ -77,6 +67,8 @@ int main(int argc, char **argv) IOR_test_t *tests_head; IOR_test_t *tptr; + mpi_comm_world = MPI_COMM_WORLD; + /* * check -h option from commandline without starting MPI; * if the help option is requested in a script file (showHelp=TRUE), @@ -504,7 +496,7 @@ static int CountErrors(IOR_param_t * test, int access, int errors) * NOTE: This also assumes that the task count on all nodes is equal * to the task count on the host running MPI task 0. */ -static int CountTasksPerNode(int numTasks, MPI_Comm comm) +int CountTasksPerNode(int numTasks, MPI_Comm comm) { /* for debugging and testing */ if (getenv("IOR_FAKE_TASK_PER_NODES")){ @@ -988,31 +980,6 @@ static void GetTestFileName(char *testFileName, IOR_param_t * test) free (fileNames); } -/* - * Get time stamp. Use MPI_Timer() unless _NO_MPI_TIMER is defined, - * in which case use gettimeofday(). - */ -static double GetTimeStamp(void) -{ - double timeVal; -#ifdef _NO_MPI_TIMER - struct timeval timer; - - if (gettimeofday(&timer, (struct timezone *)NULL) != 0) - ERR("cannot use gettimeofday()"); - timeVal = (double)timer.tv_sec + ((double)timer.tv_usec / 1000000); -#else /* not _NO_MPI_TIMER */ - timeVal = MPI_Wtime(); /* no MPI_CHECK(), just check return value */ - if (timeVal < 0) - ERR("cannot use MPI_Wtime()"); -#endif /* _NO_MPI_TIMER */ - - /* wall_clock_delta is difference from root node's time */ - timeVal -= wall_clock_delta; - - return (timeVal); -} - /* * Convert IOR_offset_t value to human readable string. This routine uses a * statically-allocated buffer internally and so is not re-entrant. @@ -1266,7 +1233,7 @@ static void PrintRemoveTiming(double start, double finish, int rep) */ static void RemoveFile(char *testFileName, int filePerProc, IOR_param_t * test) { - int tmpRankOffset; + int tmpRankOffset = 0; if (filePerProc) { /* in random tasks, delete own file */ if (test->reorderTasksRandom == TRUE) { @@ -1885,9 +1852,9 @@ static void *malloc_and_touch(size_t size) static void file_hits_histogram(IOR_param_t *params) { - int *rankoffs; - int *filecont; - int *filehits; + int *rankoffs = NULL; + int *filecont = NULL; + int *filehits = NULL; int ifile; int jfile; @@ -2614,7 +2581,7 @@ static IOR_offset_t *GetOffsetArrayRandom(IOR_param_t * test, int pretendRank, static IOR_offset_t WriteOrReadSingle(IOR_offset_t pairCnt, IOR_offset_t *offsetArray, int pretendRank, IOR_offset_t * transferCount, int * errors, IOR_param_t * test, int * fd, IOR_io_buffers* ioBuffers, int access){ - IOR_offset_t amtXferred; + IOR_offset_t amtXferred = 0; IOR_offset_t transfer; void *buffer = ioBuffers->buffer; diff --git a/src/ior.h b/src/ior.h index ce1b4ec..e01f29b 100755 --- a/src/ior.h +++ b/src/ior.h @@ -40,15 +40,6 @@ #include "iordef.h" - -extern int numTasksWorld; -extern int rank; -extern int rankOffset; -extern int tasksPerNode; -extern int verbose; -extern MPI_Comm testComm; - - /******************** DATA Packet Type ***************************************/ /* Holds the types of data packets: generic, offset, timestamp, incompressible */ @@ -91,6 +82,7 @@ typedef struct IO_BUFFERS typedef struct { + FILE * out_logfile; char debug[MAX_STR]; /* debug info string */ unsigned int mode; /* file permissions */ unsigned int openFlags; /* open flags (see also ) */ @@ -142,7 +134,7 @@ typedef struct int storeFileOffset; /* use file offset as stored signature */ int deadlineForStonewalling; /* max time in seconds to run any test phase */ int stoneWallingWearOut; /* wear out the stonewalling, once the timout is over, each process has to write the same amount */ - int stoneWallingWearOutIterations; /* the number of iterations for the stonewallingWearOut, needed for readBack */ + uint64_t stoneWallingWearOutIterations; /* the number of iterations for the stonewallingWearOut, needed for readBack */ int maxTimeDuration; /* max time in minutes to run each test */ int outlierThreshold; /* warn on outlier N seconds from mean */ int verbose; /* verbosity */ @@ -222,7 +214,13 @@ typedef struct typedef struct { double *writeTime; double *readTime; + int errors; size_t pairs_accessed; // number of I/Os done, useful for deadlineForStonewalling + + double stonewall_time; + long long stonewall_min_data_accessed; + long long stonewall_avg_data_accessed; + IOR_offset_t *aggFileSizeFromStat; IOR_offset_t *aggFileSizeFromXfer; IOR_offset_t *aggFileSizeForBW; @@ -240,5 +238,11 @@ IOR_test_t *CreateTest(IOR_param_t *init_params, int test_num); void AllocResults(IOR_test_t *test); void GetPlatformName(char *); void init_IOR_Param_t(IOR_param_t *p); +int CountTasksPerNode(int numTasks, MPI_Comm comm); + +/* + * This function runs IOR given by command line, useful for testing + */ +IOR_test_t * ior_run(int argc, char **argv, MPI_Comm world_com, FILE * out_logfile); #endif /* !_IOR_H */ diff --git a/src/mdtest-main.c b/src/mdtest-main.c new file mode 100644 index 0000000..0230a77 --- /dev/null +++ b/src/mdtest-main.c @@ -0,0 +1,10 @@ +#include "mdtest.h" + +int main(int argc, char **argv) { + MPI_Init(&argc, &argv); + + mdtest_run(argc, argv, MPI_COMM_WORLD, stdout); + + MPI_Finalize(); + return 0; +} diff --git a/src/mdtest.c b/src/mdtest.c index 52cfbb2..defd6b6 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -39,6 +39,7 @@ #include #include "getopt/optlist.h" +#include "utilities.h" #if HAVE_SYS_PARAM_H #include @@ -66,6 +67,7 @@ #include "aiori.h" #include "ior.h" +#include "mdtest.h" #include @@ -82,12 +84,9 @@ #define TEST_DIR "#test-dir" #define ITEM_COUNT 25000 -typedef struct -{ - double entry[10]; -} table_t; +#define LLU "%lu" -int rank; +//int rank; static int size; static uint64_t *rand_array; static char testdir[MAX_LEN]; @@ -109,21 +108,16 @@ static char unique_rm_dir[MAX_LEN]; static char unique_rm_uni_dir[MAX_LEN]; static char *write_buffer; static char *read_buffer; -static int barriers = 1; + +static int barriers; static int create_only; static int stat_only; static int read_only; static int remove_only; static int leaf_only; -static int branch_factor = 1; +static unsigned branch_factor; static int depth; -/* needed for MPI/IO backend to link correctly */ -int rankOffset = 0; - -/* needed for NCMPI backend to link correctly */ -int numTasksWorld = 0; - /* * This is likely a small value, but it's sometimes computed by * branch_factor^(depth+1), so we'll make it a larger variable, @@ -136,6 +130,7 @@ static uint64_t num_dirs_in_tree; * a larger variable. */ static uint64_t items_per_dir; +static int print_time; static int random_seed; static int shared_file; static int files_only; @@ -143,17 +138,17 @@ static int dirs_only; static int pre_delay; static int unique_dir_per_task; static int time_unique_dir_overhead; -int verbose; -static int throttle = 1; +static int throttle; static uint64_t items; static int collective_creates; static size_t write_bytes; +static int stone_wall_timer_seconds; static size_t read_bytes; static int sync_file; static int path_count; static int nstride; /* neighbor stride */ -MPI_Comm testComm; -static table_t * summary_table; + +static mdtest_results_t * summary_table; static pid_t pid; static uid_t uid; @@ -163,24 +158,37 @@ static const ior_aiori_t *backend; static IOR_param_t param; +/* This structure describes the processing status for stonewalling */ +typedef struct{ + double start_time; + + int stone_wall_timer_seconds; + long long unsigned items_done; + + int items_start; + uint64_t items_per_dir; +} rank_progress_t; + +#define CHECK_STONE_WALL(p) (((p)->stone_wall_timer_seconds != 0) && ((GetTimeStamp() - (p)->start_time) > (p)->stone_wall_timer_seconds)) + /* for making/removing unique directory && stating/deleting subdirectory */ enum {MK_UNI_DIR, STAT_SUB_DIR, READ_SUB_DIR, RM_SUB_DIR, RM_UNI_DIR}; #ifdef __linux__ #define FAIL(msg) do { \ - fprintf(stdout, "%s: Process %d(%s): FAILED in %s, %s: %s\n", \ + fprintf(out_logfile, "%s: Process %d(%s): FAILED in %s, %s: %s\n", \ print_timestamp(), rank, hostname, __func__, \ msg, strerror(errno)); \ - fflush(stdout); \ - MPI_Abort(MPI_COMM_WORLD, 1); \ + fflush(out_logfile); \ + MPI_Abort(testComm, 1); \ } while(0) #else #define FAIL(msg) do { \ - fprintf(stdout, "%s: Process %d(%s): FAILED at %d, %s: %s\n", \ + fprintf(out_logfile, "%s: Process %d(%s): FAILED at %d, %s: %s\n", \ print_timestamp(), rank, hostname, __LINE__, \ msg, strerror(errno)); \ - fflush(stdout); \ - MPI_Abort(MPI_COMM_WORLD, 1); \ + fflush(out_logfile); \ + MPI_Abort(testComm, 1); \ } while(0) #endif @@ -190,10 +198,10 @@ static char *print_timestamp() { if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering print_timestamp...\n" ); + fprintf( out_logfile, "V-1: Entering print_timestamp...\n" ); } - fflush(stdout); + fflush(out_logfile); cur_timestamp = time(NULL); strftime(datestring, 80, "%m/%d/%Y %T", localtime(&cur_timestamp)); @@ -206,8 +214,7 @@ int count_tasks_per_node(void) { MPI_Comm shared_comm; int rc, count; - MPI_Comm_split_type (MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, 0, MPI_INFO_NULL, - &shared_comm); + MPI_Comm_split_type (testComm, MPI_COMM_TYPE_SHARED, 0, MPI_INFO_NULL, &shared_comm); MPI_Comm_size (shared_comm, &count); @@ -224,8 +231,8 @@ int count_tasks_per_node(void) { MPI_Status status; if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering count_tasks_per_node...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering count_tasks_per_node...\n" ); + fflush( out_logfile ); } if (gethostname(localhost, MAX_LEN) != 0) { @@ -235,16 +242,16 @@ int count_tasks_per_node(void) { /* MPI_receive all hostnames, and compare to local hostname */ for (i = 0; i < size-1; i++) { MPI_Recv(hostname, MAX_LEN, MPI_CHAR, MPI_ANY_SOURCE, - MPI_ANY_TAG, MPI_COMM_WORLD, &status); + MPI_ANY_TAG, testComm, &status); if (strcmp(hostname, localhost) == 0) { count++; } } } else { /* MPI_send hostname to root node */ - MPI_Send(localhost, MAX_LEN, MPI_CHAR, 0, 0, MPI_COMM_WORLD); + MPI_Send(localhost, MAX_LEN, MPI_CHAR, 0, 0, testComm); } - MPI_Bcast(&count, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&count, 1, MPI_INT, 0, testComm); return(count); } @@ -254,14 +261,14 @@ void delay_secs(int delay) { if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering delay_secs...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering delay_secs...\n" ); + fflush( out_logfile ); } if (rank == 0 && delay > 0) { if (verbose >= 1) { - fprintf(stdout, "delaying %d seconds . . .\n", delay); - fflush(stdout); + fprintf(out_logfile, "delaying %d seconds . . .\n", delay); + fflush(out_logfile); } sleep(delay); } @@ -274,8 +281,8 @@ void offset_timers(double * t, int tcount) { if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering offset_timers...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering offset_timers...\n" ); + fflush( out_logfile ); } toffset = MPI_Wtime() - t[tcount]; @@ -291,8 +298,8 @@ void parse_dirpath(char *dirpath_arg) { if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering parse_dirpath...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering parse_dirpath...\n" ); + fflush( out_logfile ); } tmp = dirpath_arg; @@ -326,8 +333,8 @@ void unique_dir_access(int opt, char *to) { if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering unique_dir_access...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering unique_dir_access...\n" ); + fflush( out_logfile ); } if (opt == MK_UNI_DIR) { @@ -352,15 +359,15 @@ static void create_remove_dirs (const char *path, bool create, uint64_t itemNum) ( verbose >= 3 ) && (itemNum % ITEM_COUNT==0 && (itemNum != 0))) { - printf("V-3: %s dir: %llu\n", operation, itemNum); - fflush(stdout); + fprintf(out_logfile, "V-3: %s dir: "LLU"\n", operation, itemNum); + fflush(out_logfile); } //create dirs sprintf(curr_item, "%s/dir.%s%" PRIu64, path, create ? mk_name : rm_name, itemNum); if (rank == 0 && verbose >= 3) { - printf("V-3: create_remove_items_helper (dirs %s): curr_item is \"%s\"\n", operation, curr_item); - fflush(stdout); + fprintf(out_logfile, "V-3: create_remove_items_helper (dirs %s): curr_item is \"%s\"\n", operation, curr_item); + fflush(out_logfile); } if (create) { @@ -381,15 +388,15 @@ static void remove_file (const char *path, uint64_t itemNum) { ( verbose >= 3 ) && (itemNum % ITEM_COUNT==0 && (itemNum != 0))) { - printf("V-3: remove file: %llu\n", itemNum); - fflush(stdout); + fprintf(out_logfile, "V-3: remove file: "LLU"\n", itemNum); + fflush(out_logfile); } //remove files - sprintf(curr_item, "%s/file.%s%llu", path, rm_name, itemNum); + sprintf(curr_item, "%s/file.%s"LLU"", path, rm_name, itemNum); if (rank == 0 && verbose >= 3) { - printf("V-3: create_remove_items_helper (non-dirs remove): curr_item is \"%s\"\n", curr_item); - fflush(stdout); + fprintf(out_logfile, "V-3: create_remove_items_helper (non-dirs remove): curr_item is \"%s\"\n", curr_item); + fflush(out_logfile); } if (!(shared_file && rank != 0)) { @@ -405,23 +412,23 @@ static void create_file (const char *path, uint64_t itemNum) { ( verbose >= 3 ) && (itemNum % ITEM_COUNT==0 && (itemNum != 0))) { - printf("V-3: create file: %llu\n", itemNum); - fflush(stdout); + fprintf(out_logfile, "V-3: create file: "LLU"\n", itemNum); + fflush(out_logfile); } //create files - sprintf(curr_item, "%s/file.%s%llu", path, mk_name, itemNum); + sprintf(curr_item, "%s/file.%s"LLU"", path, mk_name, itemNum); if (rank == 0 && verbose >= 3) { - printf("V-3: create_remove_items_helper (non-dirs create): curr_item is \"%s\"\n", curr_item); - fflush(stdout); + fprintf(out_logfile, "V-3: create_remove_items_helper (non-dirs create): curr_item is \"%s\"\n", curr_item); + fflush(out_logfile); } if (collective_creates) { param.openFlags = IOR_WRONLY; if (rank == 0 && verbose >= 3) { - printf( "V-3: create_remove_items_helper (collective): open...\n" ); - fflush( stdout ); + fprintf(out_logfile, "V-3: create_remove_items_helper (collective): open...\n" ); + fflush( out_logfile ); } aiori_fh = backend->open (curr_item, ¶m); @@ -437,8 +444,8 @@ static void create_file (const char *path, uint64_t itemNum) { param.filePerProc = !shared_file; if (rank == 0 && verbose >= 3) { - printf( "V-3: create_remove_items_helper (non-collective, shared): open...\n" ); - fflush( stdout ); + fprintf(out_logfile, "V-3: create_remove_items_helper (non-collective, shared): open...\n" ); + fflush( out_logfile ); } aiori_fh = backend->create (curr_item, ¶m); @@ -449,8 +456,8 @@ static void create_file (const char *path, uint64_t itemNum) { if (write_bytes > 0) { if (rank == 0 && verbose >= 3) { - printf( "V-3: create_remove_items_helper: write...\n" ); - fflush( stdout ); + fprintf(out_logfile, "V-3: create_remove_items_helper: write...\n" ); + fflush( out_logfile ); } /* @@ -459,14 +466,14 @@ static void create_file (const char *path, uint64_t itemNum) { */ param.offset = 0; param.fsyncPerWrite = sync_file; - if (write_bytes != backend->xfer (WRITE, aiori_fh, (IOR_size_t *) write_buffer, write_bytes, ¶m)) { + if ( write_bytes != (size_t) backend->xfer (WRITE, aiori_fh, (IOR_size_t *) write_buffer, write_bytes, ¶m)) { FAIL("unable to write file"); } } if (rank == 0 && verbose >= 3) { - printf( "V-3: create_remove_items_helper: close...\n" ); - fflush( stdout ); + fprintf(out_logfile, "V-3: create_remove_items_helper: close...\n" ); + fflush( out_logfile ); } backend->close (aiori_fh, ¶m); @@ -474,17 +481,16 @@ static void create_file (const char *path, uint64_t itemNum) { /* helper for creating/removing items */ void create_remove_items_helper(const int dirs, const int create, const char *path, - uint64_t itemNum) { + uint64_t itemNum, rank_progress_t * progress) { char curr_item[MAX_LEN]; - if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering create_remove_items_helper...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering create_remove_items_helper...\n" ); + fflush( out_logfile ); } - for (uint64_t i = 0 ; i < items_per_dir ; ++i) { + for (uint64_t i = progress->items_start ; i < progress->items_per_dir ; ++i) { if (!dirs) { if (create) { create_file (path, itemNum + i); @@ -494,29 +500,32 @@ void create_remove_items_helper(const int dirs, const int create, const char *pa } else { create_remove_dirs (path, create, itemNum + i); } + if(CHECK_STONE_WALL(progress)){ + progress->items_done = i + 1; + return; + } } + progress->items_done = items_per_dir; } /* helper function to do collective operations */ -void collective_helper(const int dirs, const int create, const char* path, uint64_t itemNum) { - +void collective_helper(const int dirs, const int create, const char* path, uint64_t itemNum, rank_progress_t * progress) { char curr_item[MAX_LEN]; if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering collective_helper...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering collective_helper...\n" ); + fflush( out_logfile ); } - for (uint64_t i = 0 ; i < items_per_dir ; ++i) { if (dirs) { create_remove_dirs (path, create, itemNum + i); continue; } - sprintf(curr_item, "%s/file.%s%llu", path, create ? mk_name : rm_name, itemNum+i); + sprintf(curr_item, "%s/file.%s"LLU"", path, create ? mk_name : rm_name, itemNum+i); if (rank == 0 && verbose >= 3) { - printf("V-3: create file: %s\n", curr_item); - fflush(stdout); + fprintf(out_logfile, "V-3: create file: %s\n", curr_item); + fflush(out_logfile); } if (create) { @@ -534,22 +543,26 @@ void collective_helper(const int dirs, const int create, const char* path, uint6 //remove files backend->delete (curr_item, ¶m); } + if(CHECK_STONE_WALL(progress)){ + progress->items_done = i + 1; + return; + } } + progress->items_done = items_per_dir; } /* recusive function to create and remove files/directories from the directory tree */ -void create_remove_items(int currDepth, const int dirs, const int create, const int collective, - const char *path, uint64_t dirNum) { - int i; +void create_remove_items(int currDepth, const int dirs, const int create, const int collective, const char *path, uint64_t dirNum, rank_progress_t * progress) { + unsigned i; char dir[MAX_LEN]; char temp_path[MAX_LEN]; unsigned long long currDir = dirNum; if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering create_remove_items, currDepth = %d...\n", currDepth ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering create_remove_items, currDepth = %d...\n", currDepth ); + fflush( out_logfile ); } @@ -557,23 +570,23 @@ void create_remove_items(int currDepth, const int dirs, const int create, const strcpy(temp_path, path); if (rank == 0 && verbose >= 3) { - printf( "V-3: create_remove_items (start): temp_path is \"%s\"\n", temp_path ); - fflush(stdout); + fprintf(out_logfile, "V-3: create_remove_items (start): temp_path is \"%s\"\n", temp_path ); + fflush(out_logfile); } if (currDepth == 0) { /* create items at this depth */ if (!leaf_only || (depth == 0 && leaf_only)) { if (collective) { - collective_helper(dirs, create, temp_path, 0); + collective_helper(dirs, create, temp_path, 0, progress); } else { - create_remove_items_helper(dirs, create, temp_path, 0); + create_remove_items_helper(dirs, create, temp_path, 0, progress); } } if (depth > 0) { create_remove_items(++currDepth, dirs, create, - collective, temp_path, ++dirNum); + collective, temp_path, ++dirNum, progress); } } else if (currDepth <= depth) { @@ -586,16 +599,16 @@ void create_remove_items(int currDepth, const int dirs, const int create, const strcat(temp_path, dir); if (rank == 0 && verbose >= 3) { - printf( "V-3: create_remove_items (for loop): temp_path is \"%s\"\n", temp_path ); - fflush(stdout); + fprintf(out_logfile, "V-3: create_remove_items (for loop): temp_path is \"%s\"\n", temp_path ); + fflush(out_logfile); } /* create the items in this branch */ if (!leaf_only || (leaf_only && currDepth == depth)) { if (collective) { - collective_helper(dirs, create, temp_path, currDir*items_per_dir); + collective_helper(dirs, create, temp_path, currDir*items_per_dir, progress); } else { - create_remove_items_helper(dirs, create, temp_path, currDir*items_per_dir); + create_remove_items_helper(dirs, create, temp_path, currDir*items_per_dir, progress); } } @@ -606,7 +619,9 @@ void create_remove_items(int currDepth, const int dirs, const int create, const create, collective, temp_path, - ( currDir * ( unsigned long long )branch_factor ) + 1 ); + ( currDir * ( unsigned long long )branch_factor ) + 1, + progress + ); currDepth--; /* reset the path */ @@ -617,15 +632,15 @@ void create_remove_items(int currDepth, const int dirs, const int create, const } /* stats all of the items created as specified by the input parameters */ -void mdtest_stat(const int random, const int dirs, const char *path) { +void mdtest_stat(const int random, const int dirs, const char *path, rank_progress_t * progress) { struct stat buf; uint64_t parent_dir, item_num = 0; char item[MAX_LEN], temp[MAX_LEN]; uint64_t stop; if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering mdtest_stat...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering mdtest_stat...\n" ); + fflush( out_logfile ); } /* determine the number of items to stat*/ @@ -663,16 +678,16 @@ void mdtest_stat(const int random, const int dirs, const char *path) { /* create name of file/dir to stat */ if (dirs) { if (rank == 0 && verbose >= 3 && (i%ITEM_COUNT == 0) && (i != 0)) { - printf("V-3: stat dir: %llu\n", i); - fflush(stdout); + fprintf(out_logfile, "V-3: stat dir: "LLU"\n", i); + fflush(out_logfile); } - sprintf(item, "dir.%s%llu", stat_name, item_num); + sprintf(item, "dir.%s"LLU"", stat_name, item_num); } else { if (rank == 0 && verbose >= 3 && (i%ITEM_COUNT == 0) && (i != 0)) { - printf("V-3: stat file: %llu\n", i); - fflush(stdout); + fprintf(out_logfile, "V-3: stat file: "LLU"\n", i); + fflush(out_logfile); } - sprintf(item, "file.%s%llu", stat_name, item_num); + sprintf(item, "file.%s"LLU"", stat_name, item_num); } /* determine the path to the file/dir to be stat'ed */ @@ -681,13 +696,13 @@ void mdtest_stat(const int random, const int dirs, const char *path) { if (parent_dir > 0) { //item is not in tree's root directory /* prepend parent directory to item's path */ - sprintf(temp, "%s.%llu/%s", base_tree_name, parent_dir, item); + sprintf(temp, "%s."LLU"/%s", base_tree_name, parent_dir, item); strcpy(item, temp); //still not at the tree's root dir while (parent_dir > branch_factor) { parent_dir = (uint64_t) ((parent_dir-1) / branch_factor); - sprintf(temp, "%s.%llu/%s", base_tree_name, parent_dir, item); + sprintf(temp, "%s."LLU"/%s", base_tree_name, parent_dir, item); strcpy(item, temp); } } @@ -699,24 +714,24 @@ void mdtest_stat(const int random, const int dirs, const char *path) { /* below temp used to be hiername */ if (rank == 0 && verbose >= 3) { if (dirs) { - printf("V-3: mdtest_stat dir : %s\n", item); + fprintf(out_logfile, "V-3: mdtest_stat dir : %s\n", item); } else { - printf("V-3: mdtest_stat file: %s\n", item); + fprintf(out_logfile, "V-3: mdtest_stat file: %s\n", item); } - fflush(stdout); + fflush(out_logfile); } if (-1 == backend->stat (item, &buf, ¶m)) { if (dirs) { if ( verbose >= 3 ) { - fprintf( stdout, "V-3: Stat'ing directory \"%s\"\n", item ); - fflush( stdout ); + fprintf( out_logfile, "V-3: Stat'ing directory \"%s\"\n", item ); + fflush( out_logfile ); } FAIL("unable to stat directory"); } else { if ( verbose >= 3 ) { - fprintf( stdout, "V-3: Stat'ing file \"%s\"\n", item ); - fflush( stdout ); + fprintf( out_logfile, "V-3: Stat'ing file \"%s\"\n", item ); + fflush( out_logfile ); } FAIL("unable to stat file"); } @@ -732,8 +747,8 @@ void mdtest_read(int random, int dirs, char *path) { void *aiori_fh; if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering mdtest_read...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering mdtest_read...\n" ); + fflush( out_logfile ); } /* allocate read buffer */ @@ -781,10 +796,10 @@ void mdtest_read(int random, int dirs, char *path) { /* create name of file to read */ if (!dirs) { if (rank == 0 && verbose >= 3 && (i%ITEM_COUNT == 0) && (i != 0)) { - printf("V-3: read file: %llu\n", i); - fflush(stdout); + fprintf(out_logfile, "V-3: read file: "LLU"\n", i); + fflush(out_logfile); } - sprintf(item, "file.%s%llu", read_name, item_num); + sprintf(item, "file.%s"LLU"", read_name, item_num); } /* determine the path to the file/dir to be read'ed */ @@ -793,13 +808,13 @@ void mdtest_read(int random, int dirs, char *path) { if (parent_dir > 0) { //item is not in tree's root directory /* prepend parent directory to item's path */ - sprintf(temp, "%s.%llu/%s", base_tree_name, parent_dir, item); + sprintf(temp, "%s."LLU"/%s", base_tree_name, parent_dir, item); strcpy(item, temp); /* still not at the tree's root dir */ while (parent_dir > branch_factor) { parent_dir = (unsigned long long) ((parent_dir-1) / branch_factor); - sprintf(temp, "%s.%llu/%s", base_tree_name, parent_dir, item); + sprintf(temp, "%s."LLU"/%s", base_tree_name, parent_dir, item); strcpy(item, temp); } } @@ -811,9 +826,9 @@ void mdtest_read(int random, int dirs, char *path) { /* below temp used to be hiername */ if (rank == 0 && verbose >= 3) { if (!dirs) { - printf("V-3: mdtest_read file: %s\n", item); + fprintf(out_logfile, "V-3: mdtest_read file: %s\n", item); } - fflush(stdout); + fflush(out_logfile); } /* open file for reading */ @@ -825,7 +840,7 @@ void mdtest_read(int random, int dirs, char *path) { /* read file */ if (read_bytes > 0) { - if (read_bytes != backend->xfer (READ, aiori_fh, (IOR_size_t *) read_buffer, read_bytes, ¶m)) { + if (read_bytes != (size_t) backend->xfer (READ, aiori_fh, (IOR_size_t *) read_buffer, read_bytes, ¶m)) { FAIL("unable to read file"); } } @@ -837,12 +852,12 @@ void mdtest_read(int random, int dirs, char *path) { /* This method should be called by rank 0. It subsequently does all of the creates and removes for the other ranks */ -void collective_create_remove(const int create, const int dirs, const int ntasks, const char *path) { +void collective_create_remove(const int create, const int dirs, const int ntasks, const char *path, rank_progress_t * progress) { char temp[MAX_LEN]; if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering collective_create_remove...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering collective_create_remove...\n" ); + fflush( out_logfile ); } /* rank 0 does all of the creates and removes for all of the ranks */ @@ -886,11 +901,11 @@ void collective_create_remove(const int create, const int dirs, const int ntasks /* Now that everything is set up as it should be, do the create or remove */ if (rank == 0 && verbose >= 3) { - printf("V-3: collective_create_remove (create_remove_items): temp is \"%s\"\n", temp); - fflush( stdout ); + fprintf(out_logfile, "V-3: collective_create_remove (create_remove_items): temp is \"%s\"\n", temp); + fflush( out_logfile ); } - create_remove_items(0, dirs, create, 1, temp, 0); + create_remove_items(0, dirs, create, 1, temp, 0, progress); } /* reset all of the item names */ @@ -920,14 +935,16 @@ void collective_create_remove(const int create, const int dirs, const int ntasks } } -void directory_test(const int iteration, const int ntasks, const char *path) { +void directory_test(const int iteration, const int ntasks, const char *path, rank_progress_t * progress) { int size; double t[5] = {0}; char temp_path[MAX_LEN]; + MPI_Comm_size(testComm, &size); + if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering directory_test...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering directory_test...\n" ); + fflush( out_logfile ); } MPI_Barrier(testComm); @@ -945,18 +962,18 @@ void directory_test(const int iteration, const int ntasks, const char *path) { } if (verbose >= 3 && rank == 0) { - printf( "V-3: directory_test: create path is \"%s\"\n", temp_path ); - fflush( stdout ); + fprintf(out_logfile, "V-3: directory_test: create path is \"%s\"\n", temp_path ); + fflush( out_logfile ); } /* "touch" the files */ if (collective_creates) { if (rank == 0) { - collective_create_remove(1, 1, ntasks, temp_path); + collective_create_remove(1, 1, ntasks, temp_path, progress); } } else { /* create directories */ - create_remove_items(0, 1, 1, 0, temp_path, 0); + create_remove_items(0, 1, 1, 0, temp_path, 0, progress); } } @@ -977,15 +994,15 @@ void directory_test(const int iteration, const int ntasks, const char *path) { } if (verbose >= 3 && rank == 0) { - printf( "V-3: directory_test: stat path is \"%s\"\n", temp_path ); - fflush( stdout ); + fprintf(out_logfile, "V-3: directory_test: stat path is \"%s\"\n", temp_path ); + fflush( out_logfile ); } /* stat directories */ if (random_seed > 0) { - mdtest_stat(1, 1, temp_path); + mdtest_stat(1, 1, temp_path, progress); } else { - mdtest_stat(0, 1, temp_path); + mdtest_stat(0, 1, temp_path, progress); } } @@ -1006,8 +1023,8 @@ void directory_test(const int iteration, const int ntasks, const char *path) { } if (verbose >= 3 && rank == 0) { - printf( "V-3: directory_test: read path is \"%s\"\n", temp_path ); - fflush( stdout ); + fprintf(out_logfile, "V-3: directory_test: read path is \"%s\"\n", temp_path ); + fflush( out_logfile ); } /* read directories */ @@ -1034,17 +1051,18 @@ void directory_test(const int iteration, const int ntasks, const char *path) { } if (verbose >= 3 && rank == 0) { - printf( "V-3: directory_test: remove directories path is \"%s\"\n", temp_path ); - fflush( stdout ); + fprintf(out_logfile, "V-3: directory_test: remove directories path is \"%s\"\n", temp_path ); + fflush( out_logfile ); } + double start_timer = GetTimeStamp(); /* remove directories */ if (collective_creates) { if (rank == 0) { - collective_create_remove(0, 1, ntasks, temp_path); + collective_create_remove(0, 1, ntasks, temp_path, progress); } } else { - create_remove_items(0, 1, 0, 0, temp_path, 0); + create_remove_items(0, 1, 0, 0, temp_path, 0, progress); } } @@ -1061,8 +1079,8 @@ void directory_test(const int iteration, const int ntasks, const char *path) { } if (verbose >= 3 && rank == 0) { - printf( "V-3: directory_test: remove unique directories path is \"%s\"\n", temp_path ); - fflush( stdout ); + fprintf(out_logfile, "V-3: directory_test: remove unique directories path is \"%s\"\n", temp_path ); + fflush( out_logfile ); } } @@ -1070,60 +1088,63 @@ void directory_test(const int iteration, const int ntasks, const char *path) { offset_timers(t, 4); } - MPI_Comm_size(testComm, &size); - /* calculate times */ if (create_only) { - summary_table[iteration].entry[0] = items*size/(t[1] - t[0]); - } else { - summary_table[iteration].entry[0] = 0; + summary_table[iteration].rate[0] = items*size/(t[1] - t[0]); + summary_table[iteration].time[0] = t[1] - t[0]; + summary_table[iteration].items[0] = items*size; + summary_table[iteration].stonewall_last_item[0] = items; } if (stat_only) { - summary_table[iteration].entry[1] = items*size/(t[2] - t[1]); - } else { - summary_table[iteration].entry[1] = 0; + summary_table[iteration].rate[1] = items*size/(t[2] - t[1]); + summary_table[iteration].time[1] = t[2] - t[1]; + summary_table[iteration].items[1] = items*size; + summary_table[iteration].stonewall_last_item[1] = items; } if (read_only) { - summary_table[iteration].entry[2] = items*size/(t[3] - t[2]); - } else { - summary_table[iteration].entry[2] = 0; + summary_table[iteration].rate[2] = items*size/(t[3] - t[2]); + summary_table[iteration].time[2] = t[3] - t[2]; + summary_table[iteration].items[2] = items*size; + summary_table[iteration].stonewall_last_item[2] = items; } if (remove_only) { - summary_table[iteration].entry[3] = items*size/(t[4] - t[3]); - } else { - summary_table[iteration].entry[3] = 0; + summary_table[iteration].rate[3] = items*size/(t[4] - t[3]); + summary_table[iteration].time[3] = t[4] - t[3]; + summary_table[iteration].items[3] = items*size; + summary_table[iteration].stonewall_last_item[3] = items; } if (verbose >= 1 && rank == 0) { - printf("V-1: Directory creation: %14.3f sec, %14.3f ops/sec\n", - t[1] - t[0], summary_table[iteration].entry[0]); - printf("V-1: Directory stat : %14.3f sec, %14.3f ops/sec\n", - t[2] - t[1], summary_table[iteration].entry[1]); + fprintf(out_logfile, "V-1: Directory creation: %14.3f sec, %14.3f ops/sec\n", + t[1] - t[0], summary_table[iteration].rate[0]); + fprintf(out_logfile, "V-1: Directory stat : %14.3f sec, %14.3f ops/sec\n", + t[2] - t[1], summary_table[iteration].rate[1]); /* N/A - printf("V-1: Directory read : %14.3f sec, %14.3f ops/sec\n", - t[3] - t[2], summary_table[iteration].entry[2]); + fprintf(out_logfile, "V-1: Directory read : %14.3f sec, %14.3f ops/sec\n", + t[3] - t[2], summary_table[iteration].rate[2]); */ - printf("V-1: Directory removal : %14.3f sec, %14.3f ops/sec\n", - t[4] - t[3], summary_table[iteration].entry[3]); - fflush(stdout); + fprintf(out_logfile, "V-1: Directory removal : %14.3f sec, %14.3f ops/sec\n", + t[4] - t[3], summary_table[iteration].rate[3]); + fflush(out_logfile); } } -void file_test(const int iteration, const int ntasks, const char *path) { +void file_test(const int iteration, const int ntasks, const char *path, rank_progress_t * progress) { int size; double t[5] = {0}; char temp_path[MAX_LEN]; + MPI_Comm_size(testComm, &size); if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering file_test...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering file_test...\n" ); + fflush( out_logfile ); } MPI_Barrier(testComm); t[0] = MPI_Wtime(); /* create phase */ - if (create_only) { + if (create_only && ! CHECK_STONE_WALL(progress)) { if (unique_dir_per_task) { unique_dir_access(MK_UNI_DIR, temp_path); if (!time_unique_dir_overhead) { @@ -1134,29 +1155,60 @@ void file_test(const int iteration, const int ntasks, const char *path) { } if (verbose >= 3 && rank == 0) { - printf( "V-3: file_test: create path is \"%s\"\n", temp_path ); - fflush( stdout ); + fprintf(out_logfile, "V-3: file_test: create path is \"%s\"\n", temp_path ); + fflush( out_logfile ); } /* "touch" the files */ if (collective_creates) { if (rank == 0) { - collective_create_remove(1, 0, ntasks, temp_path); + collective_create_remove(1, 0, ntasks, temp_path, progress); } MPI_Barrier(testComm); } /* create files */ - create_remove_items(0, 0, 1, 0, temp_path, 0); + create_remove_items(0, 0, 1, 0, temp_path, 0, progress); + if(stone_wall_timer_seconds){ + if (verbose >= 1 ) { + fprintf( out_logfile, "V-1: rank %d stonewall hit with %lld items\n", rank, progress->items_done ); + fflush( out_logfile ); + } + long long unsigned max_iter = 0; + MPI_Allreduce(& progress->items_done, & max_iter, 1, MPI_INT, MPI_MAX, testComm); + summary_table[iteration].stonewall_time[MDTEST_FILE_CREATE_NUM] = MPI_Wtime() - t[0]; + + // continue to the maximum... + long long min_accessed = 0; + MPI_Reduce(& progress->items_done, & min_accessed, 1, MPI_LONG_LONG_INT, MPI_MIN, 0, testComm); + + long long sum_accessed = 0; + MPI_Reduce(& progress->items_done, & sum_accessed, 1, MPI_LONG_LONG_INT, MPI_SUM, 0, testComm); + + if (rank == 0 && items != sum_accessed / size) { + summary_table[iteration].stonewall_item_sum[MDTEST_FILE_CREATE_NUM] = sum_accessed; + summary_table[iteration].stonewall_item_min[MDTEST_FILE_CREATE_NUM] = min_accessed * size; + fprintf( out_logfile, "V-1: continue stonewall hit min: %lld max: %lld avg: %.1f \n", min_accessed, max_iter, ((double) sum_accessed) / size); + fflush( out_logfile ); + } + + progress->stone_wall_timer_seconds = 0; + progress->items_start = progress->items_done; + progress->items_per_dir = max_iter; + create_remove_items(0, 0, 1, 0, temp_path, 0, progress); + progress->stone_wall_timer_seconds = stone_wall_timer_seconds; + items = max_iter; + progress->items_done = max_iter; + } } if (barriers) { - MPI_Barrier(testComm); + MPI_Barrier(testComm); } t[1] = MPI_Wtime(); /* stat phase */ - if (stat_only) { + if (stat_only && ! CHECK_STONE_WALL(progress)) { if (unique_dir_per_task) { unique_dir_access(STAT_SUB_DIR, temp_path); if (!time_unique_dir_overhead) { @@ -1167,15 +1219,15 @@ void file_test(const int iteration, const int ntasks, const char *path) { } if (verbose >= 3 && rank == 0) { - printf( "V-3: file_test: stat path is \"%s\"\n", temp_path ); - fflush( stdout ); + fprintf(out_logfile, "V-3: file_test: stat path is \"%s\"\n", temp_path ); + fflush( out_logfile ); } /* stat files */ if (random_seed > 0) { - mdtest_stat(1,0,temp_path); + mdtest_stat(1,0,temp_path, progress); } else { - mdtest_stat(0,0,temp_path); + mdtest_stat(0,0,temp_path, progress); } } @@ -1185,7 +1237,7 @@ void file_test(const int iteration, const int ntasks, const char *path) { t[2] = MPI_Wtime(); /* read phase */ - if (read_only) { + if (read_only && ! CHECK_STONE_WALL(progress)) { if (unique_dir_per_task) { unique_dir_access(READ_SUB_DIR, temp_path); if (!time_unique_dir_overhead) { @@ -1196,8 +1248,8 @@ void file_test(const int iteration, const int ntasks, const char *path) { } if (verbose >= 3 && rank == 0) { - printf( "V-3: file_test: read path is \"%s\"\n", temp_path ); - fflush( stdout ); + fprintf(out_logfile, "V-3: file_test: read path is \"%s\"\n", temp_path ); + fflush( out_logfile ); } /* read files */ @@ -1213,7 +1265,7 @@ void file_test(const int iteration, const int ntasks, const char *path) { } t[3] = MPI_Wtime(); - if (remove_only) { + if (remove_only && ! CHECK_STONE_WALL(progress)) { if (unique_dir_per_task) { unique_dir_access(RM_SUB_DIR, temp_path); if (!time_unique_dir_overhead) { @@ -1224,16 +1276,16 @@ void file_test(const int iteration, const int ntasks, const char *path) { } if (verbose >= 3 && rank == 0) { - printf( "V-3: file_test: rm directories path is \"%s\"\n", temp_path ); - fflush( stdout ); + fprintf(out_logfile, "V-3: file_test: rm directories path is \"%s\"\n", temp_path ); + fflush( out_logfile ); } if (collective_creates) { if (rank == 0) { - collective_create_remove(0, 0, ntasks, temp_path); + collective_create_remove(0, 0, ntasks, temp_path, progress); } } else { - create_remove_items(0, 0, 0, 0, temp_path, 0); + create_remove_items(0, 0, 0, 0, temp_path, 0, progress); } } @@ -1241,8 +1293,7 @@ void file_test(const int iteration, const int ntasks, const char *path) { MPI_Barrier(testComm); } t[4] = MPI_Wtime(); - - if (remove_only) { + if (remove_only && ! CHECK_STONE_WALL(progress)) { if (unique_dir_per_task) { unique_dir_access(RM_UNI_DIR, temp_path); } else { @@ -1250,8 +1301,8 @@ void file_test(const int iteration, const int ntasks, const char *path) { } if (verbose >= 3 && rank == 0) { - printf( "V-3: file_test: rm unique directories path is \"%s\"\n", temp_path ); - fflush( stdout ); + fprintf(out_logfile, "V-3: file_test: rm unique directories path is \"%s\"\n", temp_path ); + fflush( out_logfile ); } } @@ -1259,52 +1310,54 @@ void file_test(const int iteration, const int ntasks, const char *path) { offset_timers(t, 4); } - MPI_Comm_size(testComm, &size); - /* calculate times */ if (create_only) { - summary_table[iteration].entry[4] = items*size/(t[1] - t[0]); - } else { - summary_table[iteration].entry[4] = 0; + summary_table[iteration].rate[4] = items*size/(t[1] - t[0]); + summary_table[iteration].time[4] = t[1] - t[0]; + summary_table[iteration].items[4] = items*size; + summary_table[iteration].stonewall_last_item[4] = items; } if (stat_only) { - summary_table[iteration].entry[5] = items*size/(t[2] - t[1]); - } else { - summary_table[iteration].entry[5] = 0; + summary_table[iteration].rate[5] = items*size/(t[2] - t[1]); + summary_table[iteration].time[5] = t[2] - t[1]; + summary_table[iteration].items[5] = items*size; + summary_table[iteration].stonewall_last_item[5] = items; } if (read_only) { - summary_table[iteration].entry[6] = items*size/(t[3] - t[2]); - } else { - summary_table[iteration].entry[6] = 0; + summary_table[iteration].rate[6] = items*size/(t[3] - t[2]); + summary_table[iteration].time[6] = t[3] - t[2]; + summary_table[iteration].items[6] = items*size; + summary_table[iteration].stonewall_last_item[6] = items; } if (remove_only) { - summary_table[iteration].entry[7] = items*size/(t[4] - t[3]); - } else { - summary_table[iteration].entry[7] = 0; + summary_table[iteration].rate[7] = items*size/(t[4] - t[3]); + summary_table[iteration].time[7] = t[4] - t[3]; + summary_table[iteration].items[7] = items*size; + summary_table[iteration].stonewall_last_item[7] = items; } if (verbose >= 1 && rank == 0) { - printf("V-1: File creation : %14.3f sec, %14.3f ops/sec\n", - t[1] - t[0], summary_table[iteration].entry[4]); - printf("V-1: File stat : %14.3f sec, %14.3f ops/sec\n", - t[2] - t[1], summary_table[iteration].entry[5]); - printf("V-1: File read : %14.3f sec, %14.3f ops/sec\n", - t[3] - t[2], summary_table[iteration].entry[6]); - printf("V-1: File removal : %14.3f sec, %14.3f ops/sec\n", - t[4] - t[3], summary_table[iteration].entry[7]); - fflush(stdout); + fprintf(out_logfile, "V-1: File creation : %14.3f sec, %14.3f ops/sec\n", + t[1] - t[0], summary_table[iteration].rate[4]); + fprintf(out_logfile, "V-1: File stat : %14.3f sec, %14.3f ops/sec\n", + t[2] - t[1], summary_table[iteration].rate[5]); + fprintf(out_logfile, "V-1: File read : %14.3f sec, %14.3f ops/sec\n", + t[3] - t[2], summary_table[iteration].rate[6]); + fprintf(out_logfile, "V-1: File removal : %14.3f sec, %14.3f ops/sec\n", + t[4] - t[3], summary_table[iteration].rate[7]); + fflush(out_logfile); } } void print_help (void) { int j; - printf ( + fprintf(out_logfile, "Usage: mdtest [-b branching_factor] [-B] [-c] [-C] [-d testdir] [-D] [-e number_of_bytes_to_read]\n" " [-E] [-f first] [-F] [-h] [-i iterations] [-I items_per_dir] [-l last] [-L]\n" " [-n number_of_items] [-N stride_length] [-p seconds] [-r]\n" " [-R[seed]] [-s stride] [-S] [-t] [-T] [-u] [-v] [-a API]\n" - " [-V verbosity_value] [-w number_of_bytes_to_write] [-y] [-z depth]\n" + " [-V verbosity_value] [-w number_of_bytes_to_write] [-W seconds] [-y] [-z depth] -Z\n" "\t-a: API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI]\n" "\t-b: branching factor of hierarchical directory structure\n" "\t-B: no barriers between phases\n" @@ -1334,8 +1387,10 @@ void print_help (void) { "\t-v: verbosity (each instance of option increments by one)\n" "\t-V: verbosity value\n" "\t-w: bytes to write to each file after it is created\n" + "\t-W: number in seconds; stonewall timer, write as many seconds and ensure all processes did the same number of operations\n" "\t-y: sync file after writing\n" "\t-z: depth of hierarchical directory structure\n" + "\t-Z: print time instead of rate\n" ); MPI_Initialized(&j); @@ -1348,30 +1403,34 @@ void print_help (void) { void summarize_results(int iterations) { char access[MAX_LEN]; int i, j, k; - int start, stop, tableSize = 10; + int start, stop, tableSize = MDTEST_LAST_NUM; double min, max, mean, sd, sum = 0, var = 0, curr = 0; double all[iterations * size * tableSize]; if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering summarize_results...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering summarize_results...\n" ); + fflush( out_logfile ); } - MPI_Barrier(MPI_COMM_WORLD); - MPI_Gather(&summary_table->entry[0], tableSize*iterations, - MPI_DOUBLE, all, tableSize*iterations, MPI_DOUBLE, - 0, MPI_COMM_WORLD); + MPI_Barrier(testComm); + for(int i=0; i < iterations; i++){ + if(print_time){ + MPI_Gather(& summary_table[i].time[0], tableSize, MPI_DOUBLE, & all[i*tableSize*size], tableSize, MPI_DOUBLE, 0, testComm); + }else{ + MPI_Gather(& summary_table[i].rate[0], tableSize, MPI_DOUBLE, & all[i*tableSize*size], tableSize, MPI_DOUBLE, 0, testComm); + } + } if (rank == 0) { - printf("\nSUMMARY: (of %d iterations)\n", iterations); - printf( + fprintf(out_logfile, "\nSUMMARY %s: (of %d iterations)\n", print_time ? "time": "rate", iterations); + fprintf(out_logfile, " Operation Max Min Mean Std Dev\n"); - printf( + fprintf(out_logfile, " --------- --- --- ---- -------\n"); - fflush(stdout); + fflush(out_logfile); /* if files only access, skip entries 0-3 (the dir tests) */ if (files_only && !dirs_only) { @@ -1405,8 +1464,7 @@ void summarize_results(int iterations) { for (j=0; j curr) { min = curr; } @@ -1518,7 +1581,13 @@ void summarize_results(int iterations) { } mean = sum / (iterations); for (j = 0; j < iterations; j++) { - var += pow((mean - summary_table[j].entry[i]), 2); + if(print_time){ + curr = summary_table[j].time[i]; + }else{ + curr = summary_table[j].rate[i]; + } + + var += pow((mean - curr), 2); } var = var / (iterations); sd = sqrt(var); @@ -1527,12 +1596,12 @@ void summarize_results(int iterations) { case 9: strcpy(access, "Tree removal :"); break; default: strcpy(access, "ERR"); break; } - printf(" %s ", access); - printf("%14.3f ", max); - printf("%14.3f ", min); - printf("%14.3f ", mean); - printf("%14.3f\n", sd); - fflush(stdout); + fprintf(out_logfile, " %s ", access); + fprintf(out_logfile, "%14.3f ", max); + fprintf(out_logfile, "%14.3f ", min); + fprintf(out_logfile, "%14.3f ", mean); + fprintf(out_logfile, "%14.3f\n", sd); + fflush(out_logfile); sum = var = 0; } } @@ -1543,8 +1612,8 @@ void valid_tests() { if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering valid_tests...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering valid_tests...\n" ); + fflush( out_logfile ); } /* if dirs_only and files_only were both left unset, set both now */ @@ -1629,8 +1698,8 @@ void show_file_system_size(char *file_system) { int ret; if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering show_file_system_size...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering show_file_system_size...\n" ); + fflush( out_logfile ); } ret = backend->statfs (file_system, &stat_buf, ¶m); @@ -1661,15 +1730,16 @@ void show_file_system_size(char *file_system) { FAIL("unable to use realpath()"); } + /* show results */ - fprintf(stdout, "Path: %s\n", real_path); - fprintf(stdout, "FS: %.1f %s Used FS: %2.1f%% ", + fprintf(out_logfile, "Path: %s\n", real_path); + fprintf(out_logfile, "FS: %.1f %s Used FS: %2.1f%% ", total_file_system_size_hr, file_system_unit_str, used_file_system_percentage); - fprintf(stdout, "Inodes: %.1f %s Used Inodes: %2.1f%%\n", + fprintf(out_logfile, "Inodes: %.1f %s Used Inodes: %2.1f%%\n", (double)total_inodes / (double)inode_unit_val, inode_unit_str, used_inode_percentage); - fflush(stdout); + fflush(out_logfile); return; } @@ -1682,13 +1752,13 @@ void display_freespace(char *testdirpath) if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering display_freespace...\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering display_freespace...\n" ); + fflush( out_logfile ); } if (verbose >= 3 && rank == 0) { - printf( "V-3: testdirpath is \"%s\"\n", testdirpath ); - fflush( stdout ); + fprintf(out_logfile, "V-3: testdirpath is \"%s\"\n", testdirpath ); + fflush( out_logfile ); } strcpy(dirpath, testdirpath); @@ -1709,30 +1779,30 @@ void display_freespace(char *testdirpath) } if (verbose >= 3 && rank == 0) { - printf( "V-3: Before show_file_system_size, dirpath is \"%s\"\n", dirpath ); - fflush( stdout ); + fprintf(out_logfile, "V-3: Before show_file_system_size, dirpath is \"%s\"\n", dirpath ); + fflush( out_logfile ); } show_file_system_size(dirpath); if (verbose >= 3 && rank == 0) { - printf( "V-3: After show_file_system_size, dirpath is \"%s\"\n", dirpath ); - fflush( stdout ); + fprintf(out_logfile, "V-3: After show_file_system_size, dirpath is \"%s\"\n", dirpath ); + fflush( out_logfile ); } return; } void create_remove_directory_tree(int create, - int currDepth, char* path, int dirNum) { + int currDepth, char* path, int dirNum, rank_progress_t * progress) { - int i; + unsigned i; char dir[MAX_LEN]; if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: Entering create_remove_directory_tree, currDepth = %d...\n", currDepth ); - fflush( stdout ); + fprintf( out_logfile, "V-1: Entering create_remove_directory_tree, currDepth = %d...\n", currDepth ); + fflush( out_logfile ); } if (currDepth == 0) { @@ -1740,21 +1810,21 @@ void create_remove_directory_tree(int create, if (create) { if (rank == 0 && verbose >= 2) { - printf("V-2: Making directory \"%s\"\n", dir); - fflush(stdout); + fprintf(out_logfile, "V-2: Making directory \"%s\"\n", dir); + fflush(out_logfile); } if (-1 == backend->mkdir (dir, DIRMODE, ¶m)) { - FAIL("Unable to create directory"); + //FAIL("Unable to create directory"); } } - create_remove_directory_tree(create, ++currDepth, dir, ++dirNum); + create_remove_directory_tree(create, ++currDepth, dir, ++dirNum, progress); if (!create) { if (rank == 0 && verbose >= 2) { - printf("V-2: Remove directory \"%s\"\n", dir); - fflush(stdout); + fprintf(out_logfile, "V-2: Remove directory \"%s\"\n", dir); + fflush(out_logfile); } if (-1 == backend->rmdir(dir, ¶m)) { @@ -1773,8 +1843,8 @@ void create_remove_directory_tree(int create, if (create) { if (rank == 0 && verbose >= 2) { - printf("V-2: Making directory \"%s\"\n", temp_path); - fflush(stdout); + fprintf(out_logfile, "V-2: Making directory \"%s\"\n", temp_path); + fflush(out_logfile); } if (-1 == backend->mkdir(temp_path, DIRMODE, ¶m)) { @@ -1783,13 +1853,13 @@ void create_remove_directory_tree(int create, } create_remove_directory_tree(create, ++currDepth, - temp_path, (branch_factor*currDir)+1); + temp_path, (branch_factor*currDir)+1, progress); currDepth--; if (!create) { if (rank == 0 && verbose >= 2) { - printf("V-2: Remove directory \"%s\"\n", temp_path); - fflush(stdout); + fprintf(out_logfile, "V-2: Remove directory \"%s\"\n", temp_path); + fflush(out_logfile); } if (-1 == backend->rmdir(temp_path, ¶m)) { @@ -1803,7 +1873,298 @@ void create_remove_directory_tree(int create, } } -int main(int argc, char **argv) { +static void mdtest_iteration(int i, int j, MPI_Group testgroup, mdtest_results_t * summary_table, rank_progress_t * progress){ + /* start and end times of directory tree create/remove */ + double startCreate, endCreate; + int k, c; + + if (rank == 0 && verbose >= 1) { + fprintf(out_logfile, "V-1: main: * iteration %d *\n", j+1); + fflush(out_logfile); + } + + int pos = sprintf(testdir, "%s", testdirpath); + if ( testdir[strlen( testdir ) - 1] != '/' ) { + pos += sprintf(& testdir[pos], "/"); + } + pos += sprintf(& testdir[pos], "%s", TEST_DIR); + pos += sprintf(& testdir[pos], ".%d", j); + + if (verbose >= 2 && rank == 0) { + fprintf(out_logfile, "V-2: main (for j loop): making testdir, \"%s\"\n", testdir ); + fflush( out_logfile ); + } + if ((rank < path_count) && backend->access(testdir, F_OK, ¶m) != 0) { + if (backend->mkdir(testdir, DIRMODE, ¶m) != 0) { + FAIL("Unable to create test directory"); + } + } + + /* create hierarchical directory structure */ + MPI_Barrier(testComm); + if (create_only) { + startCreate = MPI_Wtime(); + if (unique_dir_per_task) { + if (collective_creates && (rank == 0)) { + /* + * This is inside two loops, one of which already uses "i" and the other uses "j". + * I don't know how this ever worked. I'm changing this loop to use "k". + */ + for (k=0; k= 3 && rank == 0) { + fprintf(out_logfile, + "V-3: main (create hierarchical directory loop-collective): Calling create_remove_directory_tree with \"%s\"\n", + testdir ); + fflush( out_logfile ); + } + + /* + * Let's pass in the path to the directory we most recently made so that we can use + * full paths in the other calls. + */ + create_remove_directory_tree(1, 0, testdir, 0, progress); + if(CHECK_STONE_WALL(progress)){ + size = k; + break; + } + } + } else if (!collective_creates) { + if (verbose >= 3 && rank == 0) { + fprintf(out_logfile, + "V-3: main (create hierarchical directory loop-!collective_creates): Calling create_remove_directory_tree with \"%s\"\n", + testdir ); + fflush( out_logfile ); + } + + /* + * Let's pass in the path to the directory we most recently made so that we can use + * full paths in the other calls. + */ + create_remove_directory_tree(1, 0, testdir, 0, progress); + } + } else { + if (rank == 0) { + if (verbose >= 3 && rank == 0) { + fprintf(out_logfile, + "V-3: main (create hierarchical directory loop-!unque_dir_per_task): Calling create_remove_directory_tree with \"%s\"\n", + testdir ); + fflush( out_logfile ); + } + + /* + * Let's pass in the path to the directory we most recently made so that we can use + * full paths in the other calls. + */ + create_remove_directory_tree(1, 0 , testdir, 0, progress); + } + } + MPI_Barrier(testComm); + endCreate = MPI_Wtime(); + summary_table->rate[8] = + num_dirs_in_tree / (endCreate - startCreate); + summary_table->time[8] = (endCreate - startCreate); + summary_table->items[8] = num_dirs_in_tree; + summary_table->stonewall_last_item[8] = num_dirs_in_tree; + if (verbose >= 1 && rank == 0) { + fprintf(out_logfile, "V-1: main: Tree creation : %14.3f sec, %14.3f ops/sec\n", + (endCreate - startCreate), summary_table->rate[8]); + fflush(out_logfile); + } + } + sprintf(unique_mk_dir, "%s/%s.0", testdir, base_tree_name); + sprintf(unique_chdir_dir, "%s/%s.0", testdir, base_tree_name); + sprintf(unique_stat_dir, "%s/%s.0", testdir, base_tree_name); + sprintf(unique_read_dir, "%s/%s.0", testdir, base_tree_name); + sprintf(unique_rm_dir, "%s/%s.0", testdir, base_tree_name); + sprintf(unique_rm_uni_dir, "%s", testdir); + + if (!unique_dir_per_task) { + if (verbose >= 3 && rank == 0) { + fprintf(out_logfile, "V-3: main: Using unique_mk_dir, \"%s\"\n", unique_mk_dir ); + fflush( out_logfile ); + } + } + + if (rank < i) { + if (!shared_file) { + sprintf(mk_name, "mdtest.%d.", (rank+(0*nstride))%i); + sprintf(stat_name, "mdtest.%d.", (rank+(1*nstride))%i); + sprintf(read_name, "mdtest.%d.", (rank+(2*nstride))%i); + sprintf(rm_name, "mdtest.%d.", (rank+(3*nstride))%i); + } + if (unique_dir_per_task) { + sprintf(unique_mk_dir, "%s/mdtest_tree.%d.0", testdir, + (rank+(0*nstride))%i); + sprintf(unique_chdir_dir, "%s/mdtest_tree.%d.0", testdir, + (rank+(1*nstride))%i); + sprintf(unique_stat_dir, "%s/mdtest_tree.%d.0", testdir, + (rank+(2*nstride))%i); + sprintf(unique_read_dir, "%s/mdtest_tree.%d.0", testdir, + (rank+(3*nstride))%i); + sprintf(unique_rm_dir, "%s/mdtest_tree.%d.0", testdir, + (rank+(4*nstride))%i); + sprintf(unique_rm_uni_dir, "%s", testdir); + } + strcpy(top_dir, unique_mk_dir); + + if (verbose >= 3 && rank == 0) { + fprintf(out_logfile, "V-3: main: Copied unique_mk_dir, \"%s\", to topdir\n", unique_mk_dir ); + fflush( out_logfile ); + } + + if (dirs_only && !shared_file) { + if (pre_delay) { + delay_secs(pre_delay); + } + directory_test(j, i, unique_mk_dir, progress); + } + if (files_only) { + if (pre_delay) { + delay_secs(pre_delay); + } + file_test(j, i, unique_mk_dir, progress); + } + } + + /* remove directory structure */ + if (!unique_dir_per_task) { + if (verbose >= 3 && rank == 0) { + fprintf(out_logfile, "V-3: main: Using testdir, \"%s\"\n", testdir ); + fflush( out_logfile ); + } + } + + MPI_Barrier(testComm); + if(CHECK_STONE_WALL(progress)){ + return; + } + if (remove_only) { + startCreate = MPI_Wtime(); + if (unique_dir_per_task) { + if (collective_creates && (rank == 0)) { + /* + * This is inside two loops, one of which already uses "i" and the other uses "j". + * I don't know how this ever worked. I'm changing this loop to use "k". + */ + for (k=0; k= 3 && rank == 0) { + fprintf(out_logfile, + "V-3: main (remove hierarchical directory loop-collective): Calling create_remove_directory_tree with \"%s\"\n", + testdir ); + fflush( out_logfile ); + } + + /* + * Let's pass in the path to the directory we most recently made so that we can use + * full paths in the other calls. + */ + create_remove_directory_tree(0, 0, testdir, 0, progress); + if(CHECK_STONE_WALL(progress)){ + size = k; + break; + } + } + } else if (!collective_creates) { + if (verbose >= 3 && rank == 0) { + fprintf(out_logfile, + "V-3: main (remove hierarchical directory loop-!collective): Calling create_remove_directory_tree with \"%s\"\n", + testdir ); + fflush( out_logfile ); + } + + /* + * Let's pass in the path to the directory we most recently made so that we can use + * full paths in the other calls. + */ + create_remove_directory_tree(0, 0, testdir, 0, progress); + } + } else { + if (rank == 0) { + if (verbose >= 3 && rank == 0) { + fprintf(out_logfile, + "V-3: main (remove hierarchical directory loop-!unique_dir_per_task): Calling create_remove_directory_tree with \"%s\"\n", + testdir ); + fflush( out_logfile ); + } + + /* + * Let's pass in the path to the directory we most recently made so that we can use + * full paths in the other calls. + */ + create_remove_directory_tree(0, 0 , testdir, 0, progress); + } + } + + MPI_Barrier(testComm); + endCreate = MPI_Wtime(); + summary_table->rate[9] = num_dirs_in_tree / (endCreate - startCreate); + summary_table->time[9] = endCreate - startCreate; + summary_table->items[9] = num_dirs_in_tree; + summary_table->stonewall_last_item[8] = num_dirs_in_tree; + if (verbose >= 1 && rank == 0) { + fprintf(out_logfile, "V-1: main Tree removal : %14.3f sec, %14.3f ops/sec\n", + (endCreate - startCreate), summary_table->rate[9]); + fflush(out_logfile); + } + + if (( rank == 0 ) && ( verbose >=2 )) { + fprintf( out_logfile, "V-2: main (at end of for j loop): Removing testdir of \"%s\"\n", testdir ); + fflush( out_logfile ); + } + + if ((rank < path_count) && backend->access(testdir, F_OK, ¶m) == 0) { + //if (( rank == 0 ) && access(testdir, F_OK) == 0) { + if (backend->rmdir(testdir, ¶m) == -1) { + FAIL("unable to remove directory"); + } + } + } else { + summary_table->rate[9] = 0; + } +} + +void mdtest_init_args(){ + barriers = 1; + branch_factor = 1; + throttle = 1; + create_only = 0; + stat_only = 0; + read_only = 0; + remove_only = 0; + leaf_only = 0; + depth = 0; + num_dirs_in_tree = 0; + items_per_dir = 0; + random_seed = 0; + print_time = 0; + shared_file = 0; + files_only = 0; + dirs_only = 0; + pre_delay = 0; + unique_dir_per_task = 0; + time_unique_dir_overhead = 0; + items = 0; + collective_creates = 0; + write_bytes = 0; + stone_wall_timer_seconds = 0; + read_bytes = 0; + sync_file = 0; + path_count = 0; + nstride = 0; +} + +mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * world_out) { + testComm = world_com; + out_logfile = world_out; + mpi_comm_world = world_com; + + init_clock(); + + mdtest_init_args(); int i, j, k; int nodeCount; MPI_Group worldgroup, testgroup; @@ -1825,12 +2186,8 @@ int main(int argc, char **argv) { } } - MPI_Init(&argc, &argv); - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &size); - - /* NCMPI backend uses numTaskWorld as size */ - numTasksWorld = size; + MPI_Comm_rank(testComm, &rank); + MPI_Comm_size(testComm, &size); pid = getpid(); uid = getuid(); @@ -1838,30 +2195,32 @@ int main(int argc, char **argv) { nodeCount = size / count_tasks_per_node(); if (rank == 0) { - printf("-- started at %s --\n\n", print_timestamp()); - printf("mdtest-%s was launched with %d total task(s) on %d node(s)\n", + fprintf(out_logfile, "-- started at %s --\n\n", print_timestamp()); + fprintf(out_logfile, "mdtest-%s was launched with %d total task(s) on %d node(s)\n", RELEASE_VERS, size, nodeCount); - fflush(stdout); + fflush(out_logfile); } if (rank == 0) { - fprintf(stdout, "Command line used:"); - for (i = 0; i < argc; i++) { - fprintf(stdout, " %s", argv[i]); + fprintf(out_logfile, "Command line used: %s", argv[0]); + for (i = 1; i < argc; i++) { + fprintf(out_logfile, " \"%s\"", argv[i]); } - fprintf(stdout, "\n"); - fflush(stdout); + fprintf(out_logfile, "\n"); + fflush(out_logfile); } /* Parse command line options */ + + verbose = 0; option_t *optList, *thisOpt; optList = GetOptList(argc, argv, "a:b:BcCd:De:Ef:Fhi:I:l:Ln:N:p:rR::s:StTuvV:w:W:yz:Z"); + while (optList != NULL) { thisOpt = optList; optarg = thisOpt->argument; optList = optList->next; - switch (thisOpt->option) { case 'a': backend_name = optarg; break; @@ -1909,8 +2268,8 @@ int main(int argc, char **argv) { case 'R': if (optarg == NULL) { random_seed = time(NULL); - MPI_Barrier(MPI_COMM_WORLD); - MPI_Bcast(&random_seed, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Barrier(testComm); + MPI_Bcast(&random_seed, 1, MPI_INT, 0, testComm); random_seed += rank; } else { random_seed = atoi(optarg)+rank; @@ -1932,56 +2291,65 @@ int main(int argc, char **argv) { verbose = atoi(optarg); break; case 'w': write_bytes = ( size_t )strtoul( optarg, ( char ** )NULL, 10 ); break; - //write_bytes = atoi(optarg); break; + case 'W': + stone_wall_timer_seconds = atoi( optarg ); break; case 'y': sync_file = 1; break; case 'z': depth = atoi(optarg); break; + case 'Z': + print_time = TRUE; break; } } + if(stone_wall_timer_seconds > 0 && branch_factor > 1 || ! barriers){ + fprintf(out_logfile, "Error, stone wall timer does only work with a branch factor <= 1 and with barriers\n"); + MPI_Abort(testComm, 1); + } + if (!create_only && !stat_only && !read_only && !remove_only) { create_only = stat_only = read_only = remove_only = 1; if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( stdout, "V-1: main: Setting create/stat/read/remove_only to True\n" ); - fflush( stdout ); + fprintf( out_logfile, "V-1: main: Setting create/stat/read/remove_only to True\n" ); + fflush( out_logfile ); } } valid_tests(); if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf (stdout, "api : %s\n", backend_name); - fprintf( stdout, "barriers : %s\n", ( barriers ? "True" : "False" )); - fprintf( stdout, "collective_creates : %s\n", ( collective_creates ? "True" : "False" )); - fprintf( stdout, "create_only : %s\n", ( create_only ? "True" : "False" )); - fprintf( stdout, "dirpath(s):\n" ); + fprintf (out_logfile, "api : %s\n", backend_name); + fprintf( out_logfile, "barriers : %s\n", ( barriers ? "True" : "False" )); + fprintf( out_logfile, "collective_creates : %s\n", ( collective_creates ? "True" : "False" )); + fprintf( out_logfile, "create_only : %s\n", ( create_only ? "True" : "False" )); + fprintf( out_logfile, "dirpath(s):\n" ); for ( i = 0; i < path_count; i++ ) { - fprintf( stdout, "\t%s\n", filenames[i] ); + fprintf( out_logfile, "\t%s\n", filenames[i] ); } - fprintf( stdout, "dirs_only : %s\n", ( dirs_only ? "True" : "False" )); - fprintf( stdout, "read_bytes : %llu\n", read_bytes ); - fprintf( stdout, "read_only : %s\n", ( read_only ? "True" : "False" )); - fprintf( stdout, "first : %d\n", first ); - fprintf( stdout, "files_only : %s\n", ( files_only ? "True" : "False" )); - fprintf( stdout, "iterations : %d\n", iterations ); - fprintf( stdout, "items_per_dir : %llu\n", items_per_dir ); - fprintf( stdout, "last : %d\n", last ); - fprintf( stdout, "leaf_only : %s\n", ( leaf_only ? "True" : "False" )); - fprintf( stdout, "items : %llu\n", items ); - fprintf( stdout, "nstride : %d\n", nstride ); - fprintf( stdout, "pre_delay : %d\n", pre_delay ); - fprintf( stdout, "remove_only : %s\n", ( leaf_only ? "True" : "False" )); - fprintf( stdout, "random_seed : %d\n", random_seed ); - fprintf( stdout, "stride : %d\n", stride ); - fprintf( stdout, "shared_file : %s\n", ( shared_file ? "True" : "False" )); - fprintf( stdout, "time_unique_dir_overhead: %s\n", ( time_unique_dir_overhead ? "True" : "False" )); - fprintf( stdout, "stat_only : %s\n", ( stat_only ? "True" : "False" )); - fprintf( stdout, "unique_dir_per_task : %s\n", ( unique_dir_per_task ? "True" : "False" )); - fprintf( stdout, "write_bytes : %llu\n", write_bytes ); - fprintf( stdout, "sync_file : %s\n", ( sync_file ? "True" : "False" )); - fprintf( stdout, "depth : %d\n", depth ); - fflush( stdout ); + fprintf( out_logfile, "dirs_only : %s\n", ( dirs_only ? "True" : "False" )); + fprintf( out_logfile, "read_bytes : "LLU"\n", read_bytes ); + fprintf( out_logfile, "read_only : %s\n", ( read_only ? "True" : "False" )); + fprintf( out_logfile, "first : %d\n", first ); + fprintf( out_logfile, "files_only : %s\n", ( files_only ? "True" : "False" )); + fprintf( out_logfile, "iterations : %d\n", iterations ); + fprintf( out_logfile, "items_per_dir : "LLU"\n", items_per_dir ); + fprintf( out_logfile, "last : %d\n", last ); + fprintf( out_logfile, "leaf_only : %s\n", ( leaf_only ? "True" : "False" )); + fprintf( out_logfile, "items : "LLU"\n", items ); + fprintf( out_logfile, "nstride : %d\n", nstride ); + fprintf( out_logfile, "pre_delay : %d\n", pre_delay ); + fprintf( out_logfile, "remove_only : %s\n", ( leaf_only ? "True" : "False" )); + fprintf( out_logfile, "random_seed : %d\n", random_seed ); + fprintf( out_logfile, "stride : %d\n", stride ); + fprintf( out_logfile, "shared_file : %s\n", ( shared_file ? "True" : "False" )); + fprintf( out_logfile, "time_unique_dir_overhead: %s\n", ( time_unique_dir_overhead ? "True" : "False" )); + fprintf( out_logfile, "stone_wall_timer_seconds: %d\n", stone_wall_timer_seconds); + fprintf( out_logfile, "stat_only : %s\n", ( stat_only ? "True" : "False" )); + fprintf( out_logfile, "unique_dir_per_task : %s\n", ( unique_dir_per_task ? "True" : "False" )); + fprintf( out_logfile, "write_bytes : "LLU"\n", write_bytes ); + fprintf( out_logfile, "sync_file : %s\n", ( sync_file ? "True" : "False" )); + fprintf( out_logfile, "depth : %d\n", depth ); + fflush( out_logfile ); } /* setup total number of items and number of items per dir */ @@ -2070,7 +2438,7 @@ int main(int argc, char **argv) { /* setup directory path to work in */ if (path_count == 0) { /* special case where no directory path provided with '-d' option */ - getcwd(testdirpath, MAX_LEN); + char * dir = getcwd(testdirpath, MAX_LEN); path_count = 1; } else { strcpy(testdirpath, filenames[rank%path_count]); @@ -2090,26 +2458,26 @@ int main(int argc, char **argv) { /* display disk usage */ if (verbose >= 3 && rank == 0) { - printf( "V-3: main (before display_freespace): testdirpath is \"%s\"\n", testdirpath ); - fflush( stdout ); + fprintf(out_logfile, "V-3: main (before display_freespace): testdirpath is \"%s\"\n", testdirpath ); + fflush( out_logfile ); } if (rank == 0) display_freespace(testdirpath); if (verbose >= 3 && rank == 0) { - printf( "V-3: main (after display_freespace): testdirpath is \"%s\"\n", testdirpath ); - fflush( stdout ); + fprintf(out_logfile, "V-3: main (after display_freespace): testdirpath is \"%s\"\n", testdirpath ); + fflush( out_logfile ); } if (rank == 0) { if (random_seed > 0) { - printf("random seed: %d\n", random_seed); + fprintf(out_logfile, "random seed: %d\n", random_seed); } } if (gethostname(hostname, MAX_LEN) == -1) { perror("gethostname"); - MPI_Abort(MPI_COMM_WORLD, 2); + MPI_Abort(testComm, 2); } if (last == 0) { @@ -2118,7 +2486,15 @@ int main(int argc, char **argv) { } /* setup summary table for recording results */ - summary_table = (table_t *)malloc(iterations * sizeof(table_t)); + summary_table = (mdtest_results_t *) malloc(iterations * sizeof(mdtest_results_t)); + memset(summary_table, 0, sizeof(mdtest_results_t)); + for(int i=0; i < iterations; i++){ + for(int j=0; j < MDTEST_LAST_NUM; j++){ + summary_table[i].rate[j] = 0.0; + summary_table[i].time[j] = 0.0; + } + } + if (summary_table == NULL) { FAIL("out of memory"); } @@ -2129,292 +2505,73 @@ int main(int argc, char **argv) { sprintf(base_tree_name, "mdtest_tree"); } - /* start and end times of directory tree create/remove */ - double startCreate, endCreate; - /* default use shared directory */ strcpy(mk_name, "mdtest.shared."); strcpy(stat_name, "mdtest.shared."); strcpy(read_name, "mdtest.shared."); strcpy(rm_name, "mdtest.shared."); - MPI_Comm_group(MPI_COMM_WORLD, &worldgroup); + MPI_Comm_group(testComm, &worldgroup); + + // keep track of the current status for stonewalling + rank_progress_t progress; + memset(& progress, 0 , sizeof(progress)); + progress.start_time = GetTimeStamp(); + progress.stone_wall_timer_seconds = stone_wall_timer_seconds; + progress.items_per_dir = items_per_dir; + /* Run the tests */ for (i = first; i <= last && i <= size; i += stride) { range.last = i - 1; MPI_Group_range_incl(worldgroup, 1, (void *)&range, &testgroup); - MPI_Comm_create(MPI_COMM_WORLD, testgroup, &testComm); + MPI_Comm_create(testComm, testgroup, &testComm); if (rank == 0) { if (files_only && dirs_only) { - printf("\n%d tasks, %llu files/directories\n", i, i * items); + fprintf(out_logfile, "\n%d tasks, "LLU" files/directories\n", i, i * items); } else if (files_only) { if (!shared_file) { - printf("\n%d tasks, %llu files\n", i, i * items); + fprintf(out_logfile, "\n%d tasks, "LLU" files\n", i, i * items); } else { - printf("\n%d tasks, 1 file\n", i); + fprintf(out_logfile, "\n%d tasks, 1 file\n", i); } } else if (dirs_only) { - printf("\n%d tasks, %llu directories\n", i, i * items); + fprintf(out_logfile, "\n%d tasks, "LLU" directories\n", i, i * items); } } if (rank == 0 && verbose >= 1) { - printf("\n"); - printf(" Operation Duration Rate\n"); - printf(" --------- -------- ----\n"); + fprintf(out_logfile, "\n"); + fprintf(out_logfile, " Operation Duration Rate\n"); + fprintf(out_logfile, " --------- -------- ----\n"); } + for (j = 0; j < iterations; j++) { - if (rank == 0 && verbose >= 1) { - printf("V-1: main: * iteration %d *\n", j+1); - fflush(stdout); - } - - int pos = sprintf(testdir, "%s", testdirpath); - if ( testdir[strlen( testdir ) - 1] != '/' ) { - pos += sprintf(& testdir[pos], "/"); - } - pos += sprintf(& testdir[pos], "%s", TEST_DIR); - pos += sprintf(& testdir[pos], ".%d", j); - - if (verbose >= 2 && rank == 0) { - printf( "V-2: main (for j loop): making testdir, \"%s\"\n", testdir ); - fflush( stdout ); - } - if ((rank < path_count) && backend->access(testdir, F_OK, ¶m) != 0) { - if (backend->mkdir(testdir, DIRMODE, ¶m) != 0) { - FAIL("Unable to create test directory"); - } - } - MPI_Barrier(MPI_COMM_WORLD); - - /* create hierarchical directory structure */ - MPI_Barrier(MPI_COMM_WORLD); - if (create_only) { - startCreate = MPI_Wtime(); - if (unique_dir_per_task) { - if (collective_creates && (rank == 0)) { - /* - * This is inside two loops, one of which already uses "i" and the other uses "j". - * I don't know how this ever worked. I'm changing this loop to use "k". - */ - for (k=0; k= 3 && rank == 0) { - printf( - "V-3: main (create hierarchical directory loop-collective): Calling create_remove_directory_tree with \"%s\"\n", - testdir ); - fflush( stdout ); - } - - /* - * Let's pass in the path to the directory we most recently made so that we can use - * full paths in the other calls. - */ - create_remove_directory_tree(1, 0, testdir, 0); - } - } else if (!collective_creates) { - if (verbose >= 3 && rank == 0) { - printf( - "V-3: main (create hierarchical directory loop-!collective_creates): Calling create_remove_directory_tree with \"%s\"\n", - testdir ); - fflush( stdout ); - } - - /* - * Let's pass in the path to the directory we most recently made so that we can use - * full paths in the other calls. - */ - create_remove_directory_tree(1, 0, testdir, 0); - } - } else { - if (rank == 0) { - if (verbose >= 3 && rank == 0) { - printf( - "V-3: main (create hierarchical directory loop-!unque_dir_per_task): Calling create_remove_directory_tree with \"%s\"\n", - testdir ); - fflush( stdout ); - } - - /* - * Let's pass in the path to the directory we most recently made so that we can use - * full paths in the other calls. - */ - create_remove_directory_tree(1, 0 , testdir, 0); - } - } - MPI_Barrier(MPI_COMM_WORLD); - endCreate = MPI_Wtime(); - summary_table[j].entry[8] = - num_dirs_in_tree / (endCreate - startCreate); - if (verbose >= 1 && rank == 0) { - printf("V-1: main: Tree creation : %14.3f sec, %14.3f ops/sec\n", - (endCreate - startCreate), summary_table[j].entry[8]); - fflush(stdout); - } - } else { - summary_table[j].entry[8] = 0; - } - sprintf(unique_mk_dir, "%s/%s.0", testdir, base_tree_name); - sprintf(unique_chdir_dir, "%s/%s.0", testdir, base_tree_name); - sprintf(unique_stat_dir, "%s/%s.0", testdir, base_tree_name); - sprintf(unique_read_dir, "%s/%s.0", testdir, base_tree_name); - sprintf(unique_rm_dir, "%s/%s.0", testdir, base_tree_name); - sprintf(unique_rm_uni_dir, "%s", testdir); - - if (!unique_dir_per_task) { - if (verbose >= 3 && rank == 0) { - printf( "V-3: main: Using unique_mk_dir, \"%s\"\n", unique_mk_dir ); - fflush( stdout ); - } - } - - if (rank < i) { - if (!shared_file) { - sprintf(mk_name, "mdtest.%d.", (rank+(0*nstride))%i); - sprintf(stat_name, "mdtest.%d.", (rank+(1*nstride))%i); - sprintf(read_name, "mdtest.%d.", (rank+(2*nstride))%i); - sprintf(rm_name, "mdtest.%d.", (rank+(3*nstride))%i); - } - if (unique_dir_per_task) { - sprintf(unique_mk_dir, "%s/mdtest_tree.%d.0", testdir, - (rank+(0*nstride))%i); - sprintf(unique_chdir_dir, "%s/mdtest_tree.%d.0", testdir, - (rank+(1*nstride))%i); - sprintf(unique_stat_dir, "%s/mdtest_tree.%d.0", testdir, - (rank+(2*nstride))%i); - sprintf(unique_read_dir, "%s/mdtest_tree.%d.0", testdir, - (rank+(3*nstride))%i); - sprintf(unique_rm_dir, "%s/mdtest_tree.%d.0", testdir, - (rank+(4*nstride))%i); - sprintf(unique_rm_uni_dir, "%s", testdir); - } - strcpy(top_dir, unique_mk_dir); - - if (verbose >= 3 && rank == 0) { - printf( "V-3: main: Copied unique_mk_dir, \"%s\", to topdir\n", unique_mk_dir ); - fflush( stdout ); - } - - if (dirs_only && !shared_file) { - if (pre_delay) { - delay_secs(pre_delay); - } - directory_test(j, i, unique_mk_dir); - } - if (files_only) { - if (pre_delay) { - delay_secs(pre_delay); - } - file_test(j, i, unique_mk_dir); - } - } - - /* remove directory structure */ - if (!unique_dir_per_task) { - if (verbose >= 3 && rank == 0) { - printf( "V-3: main: Using testdir, \"%s\"\n", testdir ); - fflush( stdout ); - } - } - - MPI_Barrier(MPI_COMM_WORLD); - if (remove_only) { - startCreate = MPI_Wtime(); - if (unique_dir_per_task) { - if (collective_creates && (rank == 0)) { - /* - * This is inside two loops, one of which already uses "i" and the other uses "j". - * I don't know how this ever worked. I'm changing this loop to use "k". - */ - for (k=0; k= 3 && rank == 0) { - printf( - "V-3: main (remove hierarchical directory loop-collective): Calling create_remove_directory_tree with \"%s\"\n", - testdir ); - fflush( stdout ); - } - - /* - * Let's pass in the path to the directory we most recently made so that we can use - * full paths in the other calls. - */ - create_remove_directory_tree(0, 0, testdir, 0); - } - } else if (!collective_creates) { - if (verbose >= 3 && rank == 0) { - printf( - "V-3: main (remove hierarchical directory loop-!collective): Calling create_remove_directory_tree with \"%s\"\n", - testdir ); - fflush( stdout ); - } - - /* - * Let's pass in the path to the directory we most recently made so that we can use - * full paths in the other calls. - */ - create_remove_directory_tree(0, 0, testdir, 0); - } - } else { - if (rank == 0) { - if (verbose >= 3 && rank == 0) { - printf( - "V-3: main (remove hierarchical directory loop-!unique_dir_per_task): Calling create_remove_directory_tree with \"%s\"\n", - testdir ); - fflush( stdout ); - } - - /* - * Let's pass in the path to the directory we most recently made so that we can use - * full paths in the other calls. - */ - create_remove_directory_tree(0, 0 , testdir, 0); - } - } - - MPI_Barrier(MPI_COMM_WORLD); - endCreate = MPI_Wtime(); - summary_table[j].entry[9] = num_dirs_in_tree - / (endCreate - startCreate); - if (verbose >= 1 && rank == 0) { - printf("V-1: main Tree removal : %14.3f sec, %14.3f ops/sec\n", - (endCreate - startCreate), summary_table[j].entry[9]); - fflush(stdout); - } - - if (( rank == 0 ) && ( verbose >=2 )) { - fprintf( stdout, "V-2: main (at end of for j loop): Removing testdir of \"%s\"\n", testdir ); - fflush( stdout ); - } - - if ((rank < path_count) && backend->access(testdir, F_OK, ¶m) == 0) { - //if (( rank == 0 ) && access(testdir, F_OK) == 0) { - if (backend->rmdir(testdir, ¶m) == -1) { - FAIL("unable to remove directory"); - } - } - } else { - summary_table[j].entry[9] = 0; + mdtest_iteration(i, j, testgroup, & summary_table[j], & progress); + if(CHECK_STONE_WALL(& progress)){ + iterations = j + 1; + break; } } - + items = progress.items_done; summarize_results(iterations); if (i == 1 && stride > 1) { i = 0; } + if(CHECK_STONE_WALL(& progress)){ + break; + } } if (rank == 0) { - printf("\n-- finished at %s --\n", print_timestamp()); - fflush(stdout); + if(CHECK_STONE_WALL(& progress)){ + fprintf(out_logfile, "\n-- hit stonewall\n"); + } + fprintf(out_logfile, "\n-- finished at %s --\n", print_timestamp()); + fflush(out_logfile); } if (random_seed > 0) { free(rand_array); } - - MPI_Finalize(); - exit(0); + return summary_table; } diff --git a/src/mdtest.h b/src/mdtest.h new file mode 100644 index 0000000..897329f --- /dev/null +++ b/src/mdtest.h @@ -0,0 +1,36 @@ +#ifndef _MDTEST_H +#define _MDTEST_H + +#include +#include +#include + +typedef enum { + MDTEST_DIR_CREATE_NUM = 0, + MDTEST_DIR_STAT_NUM = 1, + MDTEST_DIR_READ_NUM = 1, + MDTEST_DIR_REMOVE_NUM = 3, + MDTEST_FILE_CREATE_NUM = 4, + MDTEST_FILE_STAT_NUM = 5, + MDTEST_FILE_READ_NUM = 6, + MDTEST_FILE_REMOVE_NUM = 7, + MDTEST_TREE_CREATE_NUM = 8, + MDTEST_TREE_REMOVE_NUM = 9, + MDTEST_LAST_NUM +} mdtest_test_num_t; + +typedef struct +{ + double rate[MDTEST_LAST_NUM]; + double time[MDTEST_LAST_NUM]; + uint64_t items[MDTEST_LAST_NUM]; + + uint64_t stonewall_last_item[MDTEST_LAST_NUM]; + double stonewall_time[MDTEST_LAST_NUM]; + uint64_t stonewall_item_min[MDTEST_LAST_NUM]; + uint64_t stonewall_item_sum[MDTEST_LAST_NUM]; +} mdtest_results_t; + +mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * out_logfile); + +#endif diff --git a/src/parse_options.c b/src/parse_options.c index cd061ed..506df33 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -21,8 +21,9 @@ #include #include -#include "getopt/optlist.h" +#include +#include "utilities.h" #include "ior.h" #include "aiori.h" #include "parse_options.h" @@ -55,6 +56,14 @@ static IOR_offset_t StringToBytes(char *size_str) case 'G': size <<= 30; break; + case 't': + case 'T': + size <<= 40; + break; + case 'p': + case 'P': + size <<= 50; + break; } } else if (rc == 0) { size = -1; @@ -102,7 +111,6 @@ static void CheckRunSettings(IOR_test_t *tests) { IOR_test_t *ptr; IOR_param_t *params; - int needRead, needWrite; for (ptr = tests; ptr != NULL; ptr = ptr->next) { params = &ptr->params; @@ -121,16 +129,13 @@ static void CheckRunSettings(IOR_test_t *tests) * of HDFS, which doesn't support opening RDWR. * (We assume int-valued params are exclusively 0 or 1.) */ - needRead = params->readFile | - params->checkRead | - params->checkWrite; /* checkWrite reads the file */ - needWrite = params->writeFile; if ((params->openFlags & IOR_RDWR) - && (needRead ^ needWrite)) - { - /* need to either read or write, but not both */ + && ((params->readFile | params->checkRead) + ^ (params->writeFile | params->checkWrite)) + && (params->openFlags & IOR_RDWR)) { + params->openFlags &= ~(IOR_RDWR); - if (needRead) { + if (params->readFile | params->checkRead) { params->openFlags |= IOR_RDONLY; params->openFlags &= ~(IOR_CREAT|IOR_EXCL); } @@ -140,7 +145,7 @@ static void CheckRunSettings(IOR_test_t *tests) /* If numTasks set to 0, use all tasks */ if (params->numTasks == 0) { - MPI_CHECK(MPI_Comm_size(MPI_COMM_WORLD, + MPI_CHECK(MPI_Comm_size(mpi_comm_world, ¶ms->numTasks), "MPI_Comm_size() error"); RecalculateExpectedFileSize(params); @@ -159,7 +164,7 @@ void DecodeDirective(char *line, IOR_param_t *params) rc = sscanf(line, " %[^=# \t\r\n] = %[^# \t\r\n] ", option, value); if (rc != 2 && rank == 0) { - fprintf(stdout, "Syntax error in configuration options: %s\n", + fprintf(out_logfile, "Syntax error in configuration options: %s\n", line); MPI_CHECK(MPI_Abort(MPI_COMM_WORLD, -1), "MPI_Abort() error"); } @@ -180,7 +185,7 @@ void DecodeDirective(char *line, IOR_param_t *params) } else if (strcasecmp(option, "stoneWallingWearOut") == 0) { params->stoneWallingWearOut = atoi(value); } else if (strcasecmp(option, "stoneWallingWearOutIterations") == 0) { - params->stoneWallingWearOutIterations = atoi(value); + params->stoneWallingWearOutIterations = atoll(value); } else if (strcasecmp(option, "maxtimeduration") == 0) { params->maxTimeDuration = atoi(value); } else if (strcasecmp(option, "outlierthreshold") == 0) { @@ -331,7 +336,7 @@ void DecodeDirective(char *line, IOR_param_t *params) params->summary_every_test = atoi(value); } else { if (rank == 0) - fprintf(stdout, "Unrecognized parameter \"%s\"\n", + fprintf(out_logfile, "Unrecognized parameter \"%s\"\n", option); MPI_CHECK(MPI_Abort(MPI_COMM_WORLD, -1), "MPI_Abort() error"); } @@ -455,13 +460,11 @@ IOR_test_t *ReadConfigScript(char *scriptName) */ IOR_test_t *ParseCommandLine(int argc, char **argv) { - static char * const opts = + char * const opts = "a:A:b:BcCd:D:eEf:FgG:hHi:Ij:J:kKl:mM:nN:o:O:pPqQ:rRs:St:T:uU:vVwWxX:YzZ"; int i; - static IOR_test_t *tests = NULL; - - /* suppress getopt() error message when a character is unrecognized */ - opterr = 0; + IOR_test_t *tests = NULL; + char * optarg; init_IOR_Param_t(&initialTestParams); GetPlatformName(initialTestParams.platform); @@ -562,7 +565,7 @@ IOR_test_t *ParseCommandLine(int argc, char **argv) initialTestParams.dataPacketType = offset; break; default: - fprintf(stdout, + fprintf(out_logfile, "Unknown arguement for -l %s generic assumed\n", optarg); break; } @@ -652,20 +655,14 @@ IOR_test_t *ParseCommandLine(int argc, char **argv) initialTestParams.reorderTasksRandom = TRUE; break; default: - fprintf(stdout, + fprintf(out_logfile, "ParseCommandLine: unknown option `-%c'.\n", optopt); } } - for (i = optind; i < argc; i++) - fprintf(stdout, "non-option argument: %s\n", argv[i]); - - /* If an IOR script was not used, initialize test queue to the defaults */ - if (tests == NULL) { - tests = CreateTest(&initialTestParams, 0); - AllocResults(tests); - } + tests = CreateTest(&initialTestParams, 0); + AllocResults(tests); CheckRunSettings(tests); diff --git a/src/utilities.c b/src/utilities.c index 783fb2e..ed6d830 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -16,10 +16,6 @@ # include "config.h" #endif -#ifdef __linux__ -# define _GNU_SOURCE /* Needed for O_DIRECT in fcntl */ -#endif /* __linux__ */ - #include #include #include @@ -51,9 +47,16 @@ extern int errno; extern int numTasks; -extern int rank; -extern int rankOffset; -extern int verbose; + +/* globals used by other files, also defined "extern" in ior.h */ +int numTasksWorld = 0; +int rank = 0; +int rankOffset = 0; +int tasksPerNode = 0; /* tasks per node */ +int verbose = VERBOSE_0; /* verbose output */ +MPI_Comm testComm; +MPI_Comm mpi_comm_world; +FILE * out_logfile; /***************************** F U N C T I O N S ******************************/ @@ -120,9 +123,9 @@ void DumpBuffer(void *buffer, to assume that it must always be */ for (i = 0; i < ((size / sizeof(IOR_size_t)) / 4); i++) { for (j = 0; j < 4; j++) { - fprintf(stdout, IOR_format" ", dumpBuf[4 * i + j]); + fprintf(out_logfile, IOR_format" ", dumpBuf[4 * i + j]); } - fprintf(stdout, "\n"); + fprintf(out_logfile, "\n"); } return; } /* DumpBuffer() */ @@ -188,7 +191,7 @@ void OutputToRoot(int numTasks, MPI_Comm comm, char *stringToDisplay) /* display strings */ if (rank == 0) { for (i = 0; i < numTasks; i++) { - fprintf(stdout, "%s\n", stringArray[i]); + fprintf(out_logfile, "%s\n", stringArray[i]); } } @@ -217,7 +220,7 @@ void ExtractHint(char *settingVal, char *valueVal, char *hintString) tmpPtr2 = (char *)strstr(settingPtr, "IOR_HINT__GPFS__"); if (tmpPtr1 == tmpPtr2) { settingPtr += strlen("IOR_HINT__GPFS__"); - fprintf(stdout, + fprintf(out_logfile, "WARNING: Unable to set GPFS hints (not implemented.)\n"); } } @@ -304,7 +307,7 @@ void ShowHints(MPI_Info * mpiHints) MPI_CHECK(MPI_Info_get(*mpiHints, key, MPI_MAX_INFO_VAL - 1, value, &flag), "cannot get info object value"); - fprintf(stdout, "\t%s = %s\n", key, value); + fprintf(out_logfile, "\t%s = %s\n", key, value); } } @@ -399,14 +402,14 @@ void ShowFileSystemSize(char *fileSystem) if (realpath(fileSystem, realPath) == NULL) { ERR("unable to use realpath()"); } - fprintf(stdout, "Path: %s\n", realPath); - fprintf(stdout, "FS: %.1f %s Used FS: %2.1f%% ", + fprintf(out_logfile, "Path: %s\n", realPath); + fprintf(out_logfile, "FS: %.1f %s Used FS: %2.1f%% ", totalFileSystemSizeHR, fileSystemUnitStr, usedFileSystemPercentage); - fprintf(stdout, "Inodes: %.1f Mi Used Inodes: %2.1f%%\n", + fprintf(out_logfile, "Inodes: %.1f Mi Used Inodes: %2.1f%%\n", (double)totalInodes / (double)(1<<20), usedInodePercentage); - fflush(stdout); + fflush(out_logfile); #endif /* !_WIN32 */ return; @@ -474,3 +477,65 @@ int uname(struct utsname *name) return 0; } #endif /* _WIN32 */ + + +double wall_clock_deviation; +double wall_clock_delta = 0; + +/* + * Get time stamp. Use MPI_Timer() unless _NO_MPI_TIMER is defined, + * in which case use gettimeofday(). + */ +double GetTimeStamp(void) +{ + double timeVal; +#ifdef _NO_MPI_TIMER + struct timeval timer; + + if (gettimeofday(&timer, (struct timezone *)NULL) != 0) + ERR("cannot use gettimeofday()"); + timeVal = (double)timer.tv_sec + ((double)timer.tv_usec / 1000000); +#else /* not _NO_MPI_TIMER */ + timeVal = MPI_Wtime(); /* no MPI_CHECK(), just check return value */ + if (timeVal < 0) + ERR("cannot use MPI_Wtime()"); +#endif /* _NO_MPI_TIMER */ + + /* wall_clock_delta is difference from root node's time */ + timeVal -= wall_clock_delta; + + return (timeVal); +} + +/* + * Determine any spread (range) between node times. + */ +static double TimeDeviation(void) +{ + double timestamp; + double min = 0; + double max = 0; + double roottimestamp; + + MPI_CHECK(MPI_Barrier(mpi_comm_world), "barrier error"); + timestamp = GetTimeStamp(); + MPI_CHECK(MPI_Reduce(×tamp, &min, 1, MPI_DOUBLE, + MPI_MIN, 0, mpi_comm_world), + "cannot reduce tasks' times"); + MPI_CHECK(MPI_Reduce(×tamp, &max, 1, MPI_DOUBLE, + MPI_MAX, 0, mpi_comm_world), + "cannot reduce tasks' times"); + + /* delta between individual nodes' time and root node's time */ + roottimestamp = timestamp; + MPI_CHECK(MPI_Bcast(&roottimestamp, 1, MPI_DOUBLE, 0, mpi_comm_world), + "cannot broadcast root's time"); + wall_clock_delta = timestamp - roottimestamp; + + return max - min; +} + +void init_clock(){ + /* check for skew between tasks' start times */ + wall_clock_deviation = TimeDeviation(); +} diff --git a/src/utilities.h b/src/utilities.h index 9f366b8..cc50e1a 100755 --- a/src/utilities.h +++ b/src/utilities.h @@ -18,6 +18,15 @@ #include #include "ior.h" +extern int numTasksWorld; +extern int rank; +extern int rankOffset; +extern int tasksPerNode; +extern int verbose; +extern MPI_Comm testComm; +extern MPI_Comm mpi_comm_world; +extern FILE * out_logfile; + void set_o_direct_flag(int *fd); char *CurrentTimeString(void); @@ -29,4 +38,9 @@ void SeedRandGen(MPI_Comm); void SetHints (MPI_Info *, char *); void ShowHints (MPI_Info *); +void init_clock(void); +double GetTimeStamp(void); + +extern double wall_clock_deviation; +extern double wall_clock_delta; #endif /* !_UTILITIES_H */ diff --git a/testing/basic-tests.sh b/testing/basic-tests.sh index adcfbc7..8615df8 100755 --- a/testing/basic-tests.sh +++ b/testing/basic-tests.sh @@ -7,9 +7,10 @@ # Example: export IOR_EXTRA="-v -v -v" IOR_MPIRUN=${IOR_MPIRUN:-mpiexec -np} -IOR_EXEC=${IOR_EXEC:-./build/src/ior} +IOR_BIN_DIR=${IOR_EXEC:-./build/src} IOR_OUT=${IOR_OUT:-./build/test} -IOR_EXTRA=${IOR_EXTRA:-./build/test} # Add global options like verbosity +IOR_EXTRA=${IOR_EXTRA:-} # Add global options like verbosity +MDTEST_EXTRA=${MDTEST_EXTRA:-} ################################################################################ mkdir -p ${IOR_OUT} @@ -21,16 +22,24 @@ if [[ ! -e ${IOR_OUT} ]]; then exit 1 fi -if [[ ! -e $IOR_EXEC ]]; then - echo "IOR Executable \"$IOR_EXEC\" does not exist! Call me from the root directory!" +if [[ ! -e ${IOR_BIN_DIR}/ior ]]; then + echo "IOR Executable \"${IOR_BIN_DIR}/ior\" does not exist! Call me from the root directory!" + exit 1 +fi + +if [[ ! -e ${IOR_BIN_DIR}/mdtest ]]; then + echo "MDTest Executable \"${IOR_BIN_DIR}/mdtest\" does not exist! Call me from the root directory!" exit 1 fi + ERRORS=0 # Number of errors detected while running I=0 -function TEST(){ - WHAT="${IOR_MPIRUN} ${@} ${IOR_EXTRA} -o /dev/shm/ior" +function IOR(){ + RANKS=$1 + shift + WHAT="${IOR_MPIRUN} $RANKS ${IOR_BIN_DIR}/ior ${@} ${IOR_EXTRA} -o /dev/shm/ior" $WHAT 1>${IOR_OUT}/$I 2>&1 if [[ $? != 0 ]]; then echo -n "ERR" @@ -42,25 +51,42 @@ function TEST(){ I=$((${I}+1)) } -TEST 1 ${IOR_EXEC} -a POSIX -w -z -F -Y -e -i1 -m -t 100k -b 1000k -TEST 1 ${IOR_EXEC} -a POSIX -w -z -F -k -e -i2 -m -t 100k -b 100k -TEST 1 ${IOR_EXEC} -a POSIX -r -z -F -k -e -i1 -m -t 100k -b 100k +function MDTEST(){ + RANKS=$1 + shift + WHAT="${IOR_MPIRUN} $RANKS ${IOR_BIN_DIR}/mdtest ${@} ${MDTEST_EXTRA} -d /dev/shm/ior" + $WHAT 1>${IOR_OUT}/$I 2>&1 + if [[ $? != 0 ]]; then + echo -n "ERR" + ERRORS=$(($ERRORS + 1)) + else + echo -n "OK " + fi + echo " $WHAT" + I=$((${I}+1)) +} -TEST 2 ${IOR_EXEC} -a POSIX -w -z -C -F -k -e -i1 -m -t 100k -b 100k -TEST 2 ${IOR_EXEC} -a POSIX -w -z -C -Q 1 -F -k -e -i1 -m -t 100k -b 100k -TEST 2 ${IOR_EXEC} -a POSIX -r -z -Z -Q 2 -F -k -e -i1 -m -t 100k -b 100k -TEST 2 ${IOR_EXEC} -a POSIX -r -z -Z -Q 3 -X 13 -F -k -e -i1 -m -t 100k -b 100k -TEST 2 ${IOR_EXEC} -a POSIX -w -z -Z -Q 1 -X -13 -F -e -i1 -m -t 100k -b 100k +MDTEST 1 -a POSIX + +IOR 1 -a POSIX -w -z -F -Y -e -i1 -m -t 100k -b 1000k +IOR 1 -a POSIX -w -z -F -k -e -i2 -m -t 100k -b 100k +IOR 1 -a POSIX -r -z -F -k -e -i1 -m -t 100k -b 100k + +IOR 2 -a POSIX -w -z -C -F -k -e -i1 -m -t 100k -b 100k +IOR 2 -a POSIX -w -z -C -Q 1 -F -k -e -i1 -m -t 100k -b 100k +IOR 2 -a POSIX -r -z -Z -Q 2 -F -k -e -i1 -m -t 100k -b 100k +IOR 2 -a POSIX -r -z -Z -Q 3 -X 13 -F -k -e -i1 -m -t 100k -b 100k +IOR 2 -a POSIX -w -z -Z -Q 1 -X -13 -F -e -i1 -m -t 100k -b 100k #shared tests -TEST 2 ${IOR_EXEC} -a POSIX -w -z -Y -e -i1 -m -t 100k -b 100k -TEST 2 ${IOR_EXEC} -a POSIX -w -k -e -i1 -m -t 100k -b 100k -TEST 2 ${IOR_EXEC} -a POSIX -r -z -k -e -i1 -m -t 100k -b 100k +IOR 2 -a POSIX -w -z -Y -e -i1 -m -t 100k -b 100k +IOR 2 -a POSIX -w -k -e -i1 -m -t 100k -b 100k +IOR 2 -a POSIX -r -z -k -e -i1 -m -t 100k -b 100k #test mutually exclusive options -TEST 2 ${IOR_EXEC} -a POSIX -w -z -k -e -i1 -m -t 100k -b 100k -TEST 2 ${IOR_EXEC} -a POSIX -w -z - -k -e -i1 -m -t 100k -b 100k -TEST 2 ${IOR_EXEC} -a POSIX -w -Z -i1 -m -t 100k -b 100k -d 0.1 +IOR 2 -a POSIX -w -z -k -e -i1 -m -t 100k -b 100k +IOR 2 -a POSIX -w -z - -k -e -i1 -m -t 100k -b 100k +IOR 2 -a POSIX -w -Z -i1 -m -t 100k -b 100k -d 0.1 if [[ ${ERRORS} == 0 ]] ; then echo "PASSED" diff --git a/testing/docker/centos7/run-test.sh b/testing/docker/centos7/run-test.sh index 6568a0e..2f5161f 100755 --- a/testing/docker/centos7/run-test.sh +++ b/testing/docker/centos7/run-test.sh @@ -21,7 +21,7 @@ function runTest(){ sudo -u testuser PATH=$PATH make || exit 1 cd /data/ - sudo -u testuser PATH=$PATH IOR_EXEC=$BUILD/$FLAVOR/src/ior IOR_OUT=$BUILD/$FLAVOR/test ./testing/basic-tests.sh + sudo -u testuser PATH=$PATH IOR_BIN_DIR=$BUILD/$FLAVOR/src IOR_OUT=$BUILD/$FLAVOR/test ./testing/basic-tests.sh ERROR=$(($ERROR + $?)) popd > /dev/null diff --git a/testing/docker/ubuntu14.04/run-test.sh b/testing/docker/ubuntu14.04/run-test.sh index 3639ae1..b8d47c2 100755 --- a/testing/docker/ubuntu14.04/run-test.sh +++ b/testing/docker/ubuntu14.04/run-test.sh @@ -21,7 +21,7 @@ function runTest(){ ln -sf $(which mpiexec.$FLAVOR) /usr/bin/mpiexec cd /data/ - sudo -u testuser IOR_EXEC=$BUILD/$FLAVOR/src/ior IOR_OUT=$BUILD/$FLAVOR/test ./testing/basic-tests.sh + sudo -u testuser IOR_BIN_DIR=$BUILD/$FLAVOR/src IOR_OUT=$BUILD/$FLAVOR/test ./testing/basic-tests.sh ERROR=$(($ERROR + $?)) popd > /dev/null diff --git a/testing/docker/ubuntu16.04/run-test.sh b/testing/docker/ubuntu16.04/run-test.sh index 03fb99c..4af54e3 100755 --- a/testing/docker/ubuntu16.04/run-test.sh +++ b/testing/docker/ubuntu16.04/run-test.sh @@ -22,7 +22,7 @@ function runTest(){ cd /data/ - sudo -u testuser IOR_EXEC=$BUILD/$FLAVOR/src/ior IOR_OUT=$BUILD/$FLAVOR/test ./testing/basic-tests.sh + sudo -u testuser IOR_BIN_DIR=$BUILD/$FLAVOR/src IOR_OUT=$BUILD/$FLAVOR/test ./testing/basic-tests.sh ERROR=$(($ERROR + $?)) popd > /dev/null From 21b8cca8cf04aef0f9b402e5e77e35f864e53956 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 11:42:21 +0100 Subject: [PATCH 18/80] IOR library version. --- src/Makefile.am | 2 +- src/ior.c | 706 ++++++++++++++++++++++------------------- src/ior.h | 3 + src/iordef.h | 2 +- testing/basic-tests.sh | 2 +- 5 files changed, 391 insertions(+), 324 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 20a1c3f..872a5a8 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,7 +10,7 @@ extraLDADD = extraLDFLAGS = extraCPPFLAGS = -ior_SOURCES = ior.c utilities.c parse_options.c getopt/optlist.c +ior_SOURCES = ior-main.c ior.c utilities.c parse_options.c getopt/optlist.c ior_LDFLAGS = ior_LDADD = ior_CPPFLAGS = diff --git a/src/ior.c b/src/ior.c index 769abfd..f4dbe41 100755 --- a/src/ior.c +++ b/src/ior.c @@ -38,9 +38,8 @@ /* file scope globals */ extern char **environ; -int totalErrorCount = 0; - -const ior_aiori_t *backend; +static int totalErrorCount; +static const ior_aiori_t *backend; static void DestroyTests(IOR_test_t *tests_head); static void DisplayUsage(char **); @@ -59,46 +58,16 @@ static void ValidateTests(IOR_param_t *); static IOR_offset_t WriteOrRead(IOR_param_t * test, IOR_results_t * results, void *fd, int access, IOR_io_buffers* ioBuffers); static void WriteTimes(IOR_param_t *, double **, int, int); -/********************************** M A I N ***********************************/ - -int main(int argc, char **argv) -{ - int i; +IOR_test_t * ior_run(int argc, char **argv, MPI_Comm world_com, FILE * world_out){ IOR_test_t *tests_head; IOR_test_t *tptr; + out_logfile = world_out; + mpi_comm_world = world_com; - mpi_comm_world = MPI_COMM_WORLD; - - /* - * check -h option from commandline without starting MPI; - * if the help option is requested in a script file (showHelp=TRUE), - * the help output will be displayed in the MPI job - */ - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-h") == 0) { - DisplayUsage(argv); - return (0); - } - } - -#ifdef USE_S3_AIORI - /* This is supposed to be done before *any* threads are created. - * Could MPI_Init() create threads (or call multi-threaded - * libraries)? We'll assume so. */ - AWS4C_CHECK( aws_init() ); -#endif - - /* start the MPI code */ - MPI_CHECK(MPI_Init(&argc, &argv), "cannot initialize MPI"); - MPI_CHECK(MPI_Comm_size(MPI_COMM_WORLD, &numTasksWorld), - "cannot get number of tasks"); - MPI_CHECK(MPI_Comm_rank(MPI_COMM_WORLD, &rank), "cannot get rank"); + MPI_CHECK(MPI_Comm_size(mpi_comm_world, &numTasksWorld), "cannot get number of tasks"); + MPI_CHECK(MPI_Comm_rank(mpi_comm_world, &rank), "cannot get rank"); PrintEarlyHeader(); - /* set error-handling */ - /*MPI_CHECK(MPI_Errhandler_set(MPI_COMM_WORLD, MPI_ERRORS_RETURN), - "cannot set errhandler"); */ - /* Sanity check, we were compiled with SOME backend, right? */ if (0 == aiori_count ()) { ERR("No IO backends compiled into ior. " @@ -108,7 +77,7 @@ int main(int argc, char **argv) /* setup tests, and validate parameters */ tests_head = SetupTests(argc, argv); verbose = tests_head->params.verbose; - tests_head->params.testComm = MPI_COMM_WORLD; + tests_head->params.testComm = world_com; /* check for commandline 'help' request */ if (rank == 0 && tests_head->params.showHelp == TRUE) { @@ -119,6 +88,7 @@ int main(int argc, char **argv) /* perform each test */ for (tptr = tests_head; tptr != NULL; tptr = tptr->next) { + totalErrorCount = 0; verbose = tptr->params.verbose; if (rank == 0 && verbose >= VERBOSE_0) { ShowTestInfo(&tptr->params); @@ -127,42 +97,133 @@ int main(int argc, char **argv) ShowTest(&tptr->params); } - // This is useful for trapping a running MPI process. While - // this is sleeping, run the script 'testing/hdfs/gdb.attach' - if (verbose >= VERBOSE_4) { - printf("\trank %d: sleeping\n", rank); - sleep(5); - printf("\trank %d: awake.\n", rank); - } TestIoSys(tptr); if(rank == 0 && tptr->params.stoneWallingWearOut){ - fprintf(stdout, "Pairs deadlineForStonewallingaccessed: %lld\n", (long long) tptr->results->pairs_accessed); + fprintf(out_logfile, "Pairs deadlineForStonewallingaccessed: %lld\n", (long long) tptr->results->pairs_accessed); } + tptr->results->errors = totalErrorCount; } - if (verbose < 0) - /* always print final summary */ - verbose = 0; PrintLongSummaryAllTests(tests_head); /* display finish time */ if (rank == 0 && verbose >= VERBOSE_0) { - fprintf(stdout, "\n"); - fprintf(stdout, "Finished: %s", CurrentTimeString()); + fprintf(out_logfile, "\n"); + fprintf(out_logfile, "Finished: %s", CurrentTimeString()); } + return tests_head; +} - DestroyTests(tests_head); - MPI_CHECK(MPI_Finalize(), "cannot finalize MPI"); -#ifdef USE_S3_AIORI - /* done once per program, after exiting all threads. - * NOTE: This fn doesn't return a value that can be checked for success. */ - aws_cleanup(); -#endif +int ior_main(int argc, char **argv) +{ + int i; + IOR_test_t *tests_head; + IOR_test_t *tptr; + out_logfile = stdout; - return (totalErrorCount); + /* + * check -h option from commandline without starting MPI; + * if the help option is requested in a script file (showHelp=TRUE), + * the help output will be displayed in the MPI job + */ + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-h") == 0) { + DisplayUsage(argv); + return (0); + } + } + + #ifdef USE_S3_AIORI + /* This is supposed to be done before *any* threads are created. + * Could MPI_Init() create threads (or call multi-threaded + * libraries)? We'll assume so. */ + AWS4C_CHECK( aws_init() ); + #endif + + /* start the MPI code */ + MPI_CHECK(MPI_Init(&argc, &argv), "cannot initialize MPI"); + + mpi_comm_world = MPI_COMM_WORLD; + MPI_CHECK(MPI_Comm_size(mpi_comm_world, &numTasksWorld), + "cannot get number of tasks"); + MPI_CHECK(MPI_Comm_rank(mpi_comm_world, &rank), "cannot get rank"); + PrintEarlyHeader(); + + /* set error-handling */ + /*MPI_CHECK(MPI_Errhandler_set(mpi_comm_world, MPI_ERRORS_RETURN), + "cannot set errhandler"); */ + + /* Sanity check, we were compiled with SOME backend, right? */ + if (0 == aiori_count ()) { + ERR("No IO backends compiled into ior. " + "Run 'configure --with-', and recompile."); + } + + /* setup tests, and validate parameters */ + tests_head = SetupTests(argc, argv); + verbose = tests_head->params.verbose; + tests_head->params.testComm = mpi_comm_world; + + /* check for commandline 'help' request */ + if (tests_head->params.showHelp == TRUE) { + if( rank == 0 ){ + DisplayUsage(argv); + } + MPI_Finalize(); + exit(0); + } + + PrintHeader(argc, argv); + + /* perform each test */ + for (tptr = tests_head; tptr != NULL; tptr = tptr->next) { + verbose = tptr->params.verbose; + if (rank == 0 && verbose >= VERBOSE_0) { + ShowTestInfo(&tptr->params); + } + if (rank == 0 && verbose >= VERBOSE_3) { + ShowTest(&tptr->params); + } + + // This is useful for trapping a running MPI process. While + // this is sleeping, run the script 'testing/hdfs/gdb.attach' + if (verbose >= VERBOSE_4) { + fprintf(out_logfile, "\trank %d: sleeping\n", rank); + sleep(5); + fprintf(out_logfile, "\trank %d: awake.\n", rank); + } + TestIoSys(tptr); + + if(rank == 0 && tptr->params.stoneWallingWearOut){ + fprintf(out_logfile, "Pairs deadlineForStonewallingaccessed: %lld\n", (long long) tptr->results->pairs_accessed); + } + } + + if (verbose < 0) + /* always print final summary */ + verbose = 0; + PrintLongSummaryAllTests(tests_head); + + /* display finish time */ + if (rank == 0 && verbose >= VERBOSE_0) { + fprintf(out_logfile, "\n"); + fprintf(out_logfile, "Finished: %s", CurrentTimeString()); + } + + DestroyTests(tests_head); + + MPI_CHECK(MPI_Finalize(), "cannot finalize MPI"); + + #ifdef USE_S3_AIORI + /* done once per program, after exiting all threads. + * NOTE: This fn doesn't return a value that can be checked for success. */ + aws_cleanup(); + #endif + + return totalErrorCount; } /***************************** F U N C T I O N S ******************************/ @@ -197,7 +258,7 @@ void init_IOR_Param_t(IOR_param_t * p) p->transferSize = 262144; p->randomSeed = -1; p->incompressibleSeed = 573; - p->testComm = MPI_COMM_WORLD; + p->testComm = mpi_comm_world; p->setAlignment = 1; p->lustre_start_ost = -1; @@ -272,10 +333,10 @@ DisplayOutliers(int numTasks, strcpy(accessString, "read"); } if (fabs(timerVal - mean) > (double)outlierThreshold) { - fprintf(stdout, "WARNING: for task %d, %s %s is %f\n", + fprintf(out_logfile, "WARNING: for task %d, %s %s is %f\n", rank, accessString, timeString, timerVal); - fprintf(stdout, " (mean=%f, stddev=%f)\n", mean, sd); - fflush(stdout); + fprintf(out_logfile, " (mean=%f, stddev=%f)\n", mean, sd); + fflush(out_logfile); } } @@ -329,17 +390,17 @@ static void CheckFileSize(IOR_test_t *test, IOR_offset_t dataMoved, int rep) != results->aggFileSizeFromXfer[rep]) || (results->aggFileSizeFromStat[rep] != results->aggFileSizeFromXfer[rep])) { - fprintf(stdout, + fprintf(out_logfile, "WARNING: Expected aggregate file size = %lld.\n", (long long) params->expectedAggFileSize); - fprintf(stdout, + fprintf(out_logfile, "WARNING: Stat() of aggregate file size = %lld.\n", (long long) results->aggFileSizeFromStat[rep]); - fprintf(stdout, + fprintf(out_logfile, "WARNING: Using actual aggregate bytes moved = %lld.\n", (long long) results->aggFileSizeFromXfer[rep]); if(params->deadlineForStonewalling){ - fprintf(stdout, + fprintf(out_logfile, "WARNING: maybe caused by deadlineForStonewalling\n"); } } @@ -377,7 +438,7 @@ CompareBuffers(void *expectedBuffer, length = size / sizeof(IOR_size_t); first = -1; if (verbose >= VERBOSE_3) { - fprintf(stdout, + fprintf(out_logfile, "[%d] At file byte offset %lld, comparing %llu-byte transfer\n", rank, test->offset, (long long)size); } @@ -385,15 +446,15 @@ CompareBuffers(void *expectedBuffer, if (testbuf[i] != goodbuf[i]) { errorCount++; if (verbose >= VERBOSE_2) { - fprintf(stdout, + fprintf(out_logfile, "[%d] At transfer buffer #%lld, index #%lld (file byte offset %lld):\n", rank, transferCount - 1, (long long)i, test->offset + (IOR_size_t) (i * sizeof(IOR_size_t))); - fprintf(stdout, "[%d] %s0x", rank, bufferLabel1); - fprintf(stdout, "%016llx\n", goodbuf[i]); - fprintf(stdout, "[%d] %s0x", rank, bufferLabel2); - fprintf(stdout, "%016llx\n", testbuf[i]); + fprintf(out_logfile, "[%d] %s0x", rank, bufferLabel1); + fprintf(out_logfile, "%016llx\n", goodbuf[i]); + fprintf(out_logfile, "[%d] %s0x", rank, bufferLabel2); + fprintf(out_logfile, "%016llx\n", testbuf[i]); } if (!inError) { inError = 1; @@ -403,47 +464,47 @@ CompareBuffers(void *expectedBuffer, last = i; } } else if (verbose >= VERBOSE_5 && i % 4 == 0) { - fprintf(stdout, + fprintf(out_logfile, "[%d] PASSED offset = %lld bytes, transfer %lld\n", rank, ((i * sizeof(unsigned long long)) + test->offset), transferCount); - fprintf(stdout, "[%d] GOOD %s0x", rank, bufferLabel1); + fprintf(out_logfile, "[%d] GOOD %s0x", rank, bufferLabel1); for (j = 0; j < 4; j++) - fprintf(stdout, "%016llx ", goodbuf[i + j]); - fprintf(stdout, "\n[%d] GOOD %s0x", rank, bufferLabel2); + fprintf(out_logfile, "%016llx ", goodbuf[i + j]); + fprintf(out_logfile, "\n[%d] GOOD %s0x", rank, bufferLabel2); for (j = 0; j < 4; j++) - fprintf(stdout, "%016llx ", testbuf[i + j]); - fprintf(stdout, "\n"); + fprintf(out_logfile, "%016llx ", testbuf[i + j]); + fprintf(out_logfile, "\n"); } } if (inError) { inError = 0; GetTestFileName(testFileName, test); - fprintf(stdout, + fprintf(out_logfile, "[%d] FAILED comparison of buffer containing %d-byte ints:\n", rank, (int)sizeof(unsigned long long int)); - fprintf(stdout, "[%d] File name = %s\n", rank, testFileName); - fprintf(stdout, "[%d] In transfer %lld, ", rank, + fprintf(out_logfile, "[%d] File name = %s\n", rank, testFileName); + fprintf(out_logfile, "[%d] In transfer %lld, ", rank, transferCount); - fprintf(stdout, + fprintf(out_logfile, "%lld errors between buffer indices %lld and %lld.\n", (long long)errorCount, (long long)first, (long long)last); - fprintf(stdout, "[%d] File byte offset = %lld:\n", rank, + fprintf(out_logfile, "[%d] File byte offset = %lld:\n", rank, ((first * sizeof(unsigned long long)) + test->offset)); - fprintf(stdout, "[%d] %s0x", rank, bufferLabel1); + fprintf(out_logfile, "[%d] %s0x", rank, bufferLabel1); for (j = first; j < length && j < first + 4; j++) - fprintf(stdout, "%016llx ", goodbuf[j]); + fprintf(out_logfile, "%016llx ", goodbuf[j]); if (j == length) - fprintf(stdout, "[end of buffer]"); - fprintf(stdout, "\n[%d] %s0x", rank, bufferLabel2); + fprintf(out_logfile, "[end of buffer]"); + fprintf(out_logfile, "\n[%d] %s0x", rank, bufferLabel2); for (j = first; j < length && j < first + 4; j++) - fprintf(stdout, "%016llx ", testbuf[j]); + fprintf(out_logfile, "%016llx ", testbuf[j]); if (j == length) - fprintf(stdout, "[end of buffer]"); - fprintf(stdout, "\n"); + fprintf(out_logfile, "[end of buffer]"); + fprintf(out_logfile, "\n"); if (test->quitOnError == TRUE) ERR("data check error, aborting execution"); } @@ -471,9 +532,9 @@ static int CountErrors(IOR_param_t * test, int access, int errors) WARN("overflow in errors counted"); allErrors = -1; } - fprintf(stdout, "WARNING: incorrect data on %s (%d errors found).\n", + fprintf(out_logfile, "WARNING: incorrect data on %s (%d errors found).\n", access == WRITECHECK ? "write" : "read", allErrors); - fprintf(stdout, + fprintf(out_logfile, "Used Time Stamp %u (0x%x) for Data Signature\n", test->timeStampSignatureValue, test->timeStampSignatureValue); @@ -681,7 +742,7 @@ static void DelaySecs(int delay) { if (rank == 0 && delay > 0) { if (verbose >= VERBOSE_1) - fprintf(stdout, "delaying %d seconds . . .\n", delay); + fprintf(out_logfile, "delaying %d seconds . . .\n", delay); sleep(delay); } } @@ -784,9 +845,9 @@ static void DisplayUsage(char **argv) }; int i = 0; - fprintf(stdout, "Usage: %s [OPTIONS]\n\n", *argv); + fprintf(out_logfile, "Usage: %s [OPTIONS]\n\n", *argv); for (i = 0; strlen(opts[i]) > 0; i++) - fprintf(stdout, "%s\n", opts[i]); + fprintf(out_logfile, "%s\n", opts[i]); return; } @@ -987,30 +1048,41 @@ static void GetTestFileName(char *testFileName, IOR_param_t * test) static char *HumanReadable(IOR_offset_t value, int base) { static char valueStr[MAX_STR]; - int m = 0, g = 0; - char m_str[8], g_str[8]; + IOR_offset_t m = 0, g = 0, t = 0; + char m_str[8], g_str[8], t_str[8]; if (base == BASE_TWO) { m = MEBIBYTE; g = GIBIBYTE; + t = GIBIBYTE * 1024llu; strcpy(m_str, "MiB"); strcpy(g_str, "GiB"); + strcpy(t_str, "TiB"); } else if (base == BASE_TEN) { m = MEGABYTE; g = GIGABYTE; + t = GIGABYTE * 1000llu; strcpy(m_str, "MB"); strcpy(g_str, "GB"); + strcpy(t_str, "TB"); } - if (value >= g) { - if (value % (IOR_offset_t) g) { + if (value >= t) { + if (value % t) { + snprintf(valueStr, MAX_STR-1, "%.2f %s", + (double)((double)value / t), t_str); + } else { + snprintf(valueStr, MAX_STR-1, "%d %s", (int)(value / t), t_str); + } + }else if (value >= g) { + if (value % g) { snprintf(valueStr, MAX_STR-1, "%.2f %s", (double)((double)value / g), g_str); } else { snprintf(valueStr, MAX_STR-1, "%d %s", (int)(value / g), g_str); } } else if (value >= m) { - if (value % (IOR_offset_t) m) { + if (value % m) { snprintf(valueStr, MAX_STR-1, "%.2f %s", (double)((double)value / m), m_str); } else { @@ -1075,7 +1147,7 @@ static void PPDouble(int leftjustify, double number, char *append) int precision; if (number < 0) { - fprintf(stdout, " - %s", append); + fprintf(out_logfile, " - %s", append); return; } @@ -1090,7 +1162,7 @@ static void PPDouble(int leftjustify, double number, char *append) leftjustify ? "-" : "", width, precision); - printf(format, number, append); + fprintf(out_logfile, format, number, append); } /* @@ -1203,7 +1275,7 @@ static void ReduceIterResults(IOR_test_t *test, double **timer, int rep, return; } - fprintf(stdout, "%-10s", access == WRITE ? "write" : "read"); + fprintf(out_logfile, "%-10s", access == WRITE ? "write" : "read"); bw = (double)test->results->aggFileSizeForBW[rep] / totalTime; PPDouble(LEFT, bw / MEBIBYTE, " "); PPDouble(LEFT, (double)test->params.blockSize / KIBIBYTE, " "); @@ -1212,9 +1284,9 @@ static void ReduceIterResults(IOR_test_t *test, double **timer, int rep, PPDouble(LEFT, diff_subset[1], " "); PPDouble(LEFT, diff_subset[2], " "); PPDouble(LEFT, totalTime, " "); - fprintf(stdout, "%-4d\n", rep); + fprintf(out_logfile, "%-4d\n", rep); - fflush(stdout); + fflush(out_logfile); } static void PrintRemoveTiming(double start, double finish, int rep) @@ -1222,9 +1294,9 @@ static void PrintRemoveTiming(double start, double finish, int rep) if (rank != 0 || verbose < VERBOSE_0) return; - printf("remove - - - - - - "); + fprintf(out_logfile, "remove - - - - - - "); PPDouble(1, finish-start, " "); - printf("%-4d\n", rep); + fprintf(out_logfile, "%-4d\n", rep); } /* @@ -1261,34 +1333,6 @@ static void RemoveFile(char *testFileName, int filePerProc, IOR_param_t * test) } } -/* - * Determine any spread (range) between node times. - */ -static double TimeDeviation(void) -{ - double timestamp; - double min = 0; - double max = 0; - double roottimestamp; - - MPI_CHECK(MPI_Barrier(MPI_COMM_WORLD), "barrier error"); - timestamp = GetTimeStamp(); - MPI_CHECK(MPI_Reduce(×tamp, &min, 1, MPI_DOUBLE, - MPI_MIN, 0, MPI_COMM_WORLD), - "cannot reduce tasks' times"); - MPI_CHECK(MPI_Reduce(×tamp, &max, 1, MPI_DOUBLE, - MPI_MAX, 0, MPI_COMM_WORLD), - "cannot reduce tasks' times"); - - /* delta between individual nodes' time and root node's time */ - roottimestamp = timestamp; - MPI_CHECK(MPI_Bcast(&roottimestamp, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD), - "cannot broadcast root's time"); - wall_clock_delta = timestamp - roottimestamp; - - return max - min; -} - /* * Setup tests by parsing commandline and creating test script. * Perform a sanity-check on the configured parameters. @@ -1298,13 +1342,13 @@ static IOR_test_t *SetupTests(int argc, char **argv) IOR_test_t *tests, *testsHead; /* count the tasks per node */ - tasksPerNode = CountTasksPerNode(numTasksWorld, MPI_COMM_WORLD); + tasksPerNode = CountTasksPerNode(numTasksWorld, mpi_comm_world); testsHead = tests = ParseCommandLine(argc, argv); /* * Since there is no guarantee that anyone other than * task 0 has the environment settings for the hints, pass - * the hint=value pair to everyone else in MPI_COMM_WORLD + * the hint=value pair to everyone else in mpi_comm_world */ DistributeHints(); @@ -1314,11 +1358,10 @@ static IOR_test_t *SetupTests(int argc, char **argv) tests = tests->next; } - /* check for skew between tasks' start times */ - wall_clock_deviation = TimeDeviation(); + init_clock(); /* seed random number generator */ - SeedRandGen(MPI_COMM_WORLD); + SeedRandGen(mpi_comm_world); return (testsHead); } @@ -1369,9 +1412,9 @@ static void PrintEarlyHeader() if (rank != 0) return; - printf("IOR-" META_VERSION ": MPI Coordinated Test of Parallel I/O\n"); - printf("\n"); - fflush(stdout); + fprintf(out_logfile, "IOR-" META_VERSION ": MPI Coordinated Test of Parallel I/O\n"); + fprintf(out_logfile, "\n"); + fflush(out_logfile); } static void PrintHeader(int argc, char **argv) @@ -1382,48 +1425,48 @@ static void PrintHeader(int argc, char **argv) if (rank != 0) return; - fprintf(stdout, "Began: %s", CurrentTimeString()); - fprintf(stdout, "Command line used:"); - for (i = 0; i < argc; i++) { - fprintf(stdout, " %s", argv[i]); + fprintf(out_logfile, "Began: %s", CurrentTimeString()); + fprintf(out_logfile, "Command line used: %s", argv[0]); + for (i = 1; i < argc; i++) { + fprintf(out_logfile, " \"%s\"", argv[i]); } - fprintf(stdout, "\n"); + fprintf(out_logfile, "\n"); if (uname(&unamebuf) != 0) { EWARN("uname failed"); - fprintf(stdout, "Machine: Unknown"); + fprintf(out_logfile, "Machine: Unknown"); } else { - fprintf(stdout, "Machine: %s %s", unamebuf.sysname, + fprintf(out_logfile, "Machine: %s %s", unamebuf.sysname, unamebuf.nodename); if (verbose >= VERBOSE_2) { - fprintf(stdout, " %s %s %s", unamebuf.release, + fprintf(out_logfile, " %s %s %s", unamebuf.release, unamebuf.version, unamebuf.machine); } } - fprintf(stdout, "\n"); + fprintf(out_logfile, "\n"); #ifdef _NO_MPI_TIMER if (verbose >= VERBOSE_2) - fprintf(stdout, "Using unsynchronized POSIX timer\n"); + fprintf(out_logfile, "Using unsynchronized POSIX timer\n"); #else /* not _NO_MPI_TIMER */ if (MPI_WTIME_IS_GLOBAL) { if (verbose >= VERBOSE_2) - fprintf(stdout, "Using synchronized MPI timer\n"); + fprintf(out_logfile, "Using synchronized MPI timer\n"); } else { if (verbose >= VERBOSE_2) - fprintf(stdout, "Using unsynchronized MPI timer\n"); + fprintf(out_logfile, "Using unsynchronized MPI timer\n"); } #endif /* _NO_MPI_TIMER */ if (verbose >= VERBOSE_1) { - fprintf(stdout, "Start time skew across all tasks: %.02f sec\n", + fprintf(out_logfile, "Start time skew across all tasks: %.02f sec\n", wall_clock_deviation); } if (verbose >= VERBOSE_3) { /* show env */ - fprintf(stdout, "STARTING ENVIRON LOOP\n"); + fprintf(out_logfile, "STARTING ENVIRON LOOP\n"); for (i = 0; environ[i] != NULL; i++) { - fprintf(stdout, "%s\n", environ[i]); + fprintf(out_logfile, "%s\n", environ[i]); } - fprintf(stdout, "ENDING ENVIRON LOOP\n"); + fprintf(out_logfile, "ENDING ENVIRON LOOP\n"); } - fflush(stdout); + fflush(out_logfile); } /* @@ -1431,15 +1474,15 @@ static void PrintHeader(int argc, char **argv) */ static void ShowTestInfo(IOR_param_t *params) { - fprintf(stdout, "\n"); - fprintf(stdout, "Test %d started: %s", params->id, CurrentTimeString()); + fprintf(out_logfile, "\n"); + fprintf(out_logfile, "Test %d started: %s", params->id, CurrentTimeString()); if (verbose >= VERBOSE_1) { /* if pvfs2:, then skip */ if (Regex(params->testFileName, "^[a-z][a-z].*:") == 0) { DisplayFreespace(params); } } - fflush(stdout); + fflush(out_logfile); } /* @@ -1449,79 +1492,79 @@ static void ShowSetup(IOR_param_t *params) { if (strcmp(params->debug, "") != 0) { - printf("\n*** DEBUG MODE ***\n"); - printf("*** %s ***\n\n", params->debug); + fprintf(out_logfile, "\n*** DEBUG MODE ***\n"); + fprintf(out_logfile, "*** %s ***\n\n", params->debug); } - printf("Summary:\n"); - printf("\tapi = %s\n", params->apiVersion); - printf("\ttest filename = %s\n", params->testFileName); - printf("\taccess = "); - printf(params->filePerProc ? "file-per-process" : "single-shared-file"); - if (verbose >= VERBOSE_1 && strcasecmp(params->api, "POSIX") != 0) { - printf(params->collective == FALSE ? ", independent" : ", collective"); + fprintf(out_logfile, "Summary:\n"); + fprintf(out_logfile, "\tapi = %s\n", params->apiVersion); + fprintf(out_logfile, "\ttest filename = %s\n", params->testFileName); + fprintf(out_logfile, "\taccess = "); + fprintf(out_logfile, params->filePerProc ? "file-per-process" : "single-shared-file"); + if (verbose >= VERBOSE_1 && strcmp(params->api, "POSIX") != 0) { + fprintf(out_logfile, params->collective == FALSE ? ", independent" : ", collective"); } - printf("\n"); + fprintf(out_logfile, "\n"); if (verbose >= VERBOSE_1) { if (params->segmentCount > 1) { - fprintf(stdout, + fprintf(out_logfile, "\tpattern = strided (%d segments)\n", (int)params->segmentCount); } else { - fprintf(stdout, + fprintf(out_logfile, "\tpattern = segmented (1 segment)\n"); } } - printf("\tordering in a file ="); + fprintf(out_logfile, "\tordering in a file ="); if (params->randomOffset == FALSE) { - printf(" sequential offsets\n"); + fprintf(out_logfile, " sequential offsets\n"); } else { - printf(" random offsets\n"); + fprintf(out_logfile, " random offsets\n"); } - printf("\tordering inter file="); + fprintf(out_logfile, "\tordering inter file="); if (params->reorderTasks == FALSE && params->reorderTasksRandom == FALSE) { - printf(" no tasks offsets\n"); + fprintf(out_logfile, " no tasks offsets\n"); } if (params->reorderTasks == TRUE) { - printf(" constant task offsets = %d\n", + fprintf(out_logfile, " constant task offsets = %d\n", params->taskPerNodeOffset); } if (params->reorderTasksRandom == TRUE) { - printf(" random task offsets >= %d, seed=%d\n", + fprintf(out_logfile, " random task offsets >= %d, seed=%d\n", params->taskPerNodeOffset, params->reorderTasksRandomSeed); } - printf("\tclients = %d (%d per node)\n", + fprintf(out_logfile, "\tclients = %d (%d per node)\n", params->numTasks, params->tasksPerNode); if (params->memoryPerTask != 0) - printf("\tmemoryPerTask = %s\n", + fprintf(out_logfile, "\tmemoryPerTask = %s\n", HumanReadable(params->memoryPerTask, BASE_TWO)); if (params->memoryPerNode != 0) - printf("\tmemoryPerNode = %s\n", + fprintf(out_logfile, "\tmemoryPerNode = %s\n", HumanReadable(params->memoryPerNode, BASE_TWO)); - printf("\trepetitions = %d\n", params->repetitions); - printf("\txfersize = %s\n", + fprintf(out_logfile, "\trepetitions = %d\n", params->repetitions); + fprintf(out_logfile, "\txfersize = %s\n", HumanReadable(params->transferSize, BASE_TWO)); - printf("\tblocksize = %s\n", + fprintf(out_logfile, "\tblocksize = %s\n", HumanReadable(params->blockSize, BASE_TWO)); - printf("\taggregate filesize = %s\n", + fprintf(out_logfile, "\taggregate filesize = %s\n", HumanReadable(params->expectedAggFileSize, BASE_TWO)); #ifdef HAVE_LUSTRE_LUSTRE_USER_H if (params->lustre_set_striping) { - printf("\tLustre stripe size = %s\n", + fprintf(out_logfile, "\tLustre stripe size = %s\n", ((params->lustre_stripe_size == 0) ? "Use default" : HumanReadable(params->lustre_stripe_size, BASE_TWO))); if (params->lustre_stripe_count == 0) { - printf("\t stripe count = %s\n", "Use default"); + fprintf(out_logfile, "\t stripe count = %s\n", "Use default"); } else { - printf("\t stripe count = %d\n", + fprintf(out_logfile, "\t stripe count = %d\n", params->lustre_stripe_count); } } #endif /* HAVE_LUSTRE_LUSTRE_USER_H */ if (params->deadlineForStonewalling > 0) { - printf("\tUsing stonewalling = %d second(s)%s\n", + fprintf(out_logfile, "\tUsing stonewalling = %d second(s)%s\n", params->deadlineForStonewalling, params->stoneWallingWearOut ? " with phase out" : ""); } - fflush(stdout); + fflush(out_logfile); } /* @@ -1531,77 +1574,77 @@ static void ShowTest(IOR_param_t * test) { const char* data_packets[] = {"g", "t","o","i"}; - fprintf(stdout, "TEST:\t%s=%d\n", "id", test->id); - fprintf(stdout, "\t%s=%d\n", "refnum", test->referenceNumber); - fprintf(stdout, "\t%s=%s\n", "api", test->api); - fprintf(stdout, "\t%s=%s\n", "platform", test->platform); - fprintf(stdout, "\t%s=%s\n", "testFileName", test->testFileName); - fprintf(stdout, "\t%s=%s\n", "hintsFileName", test->hintsFileName); - fprintf(stdout, "\t%s=%d\n", "deadlineForStonewall", + fprintf(out_logfile, "TEST:\t%s=%d\n", "id", test->id); + fprintf(out_logfile, "\t%s=%d\n", "refnum", test->referenceNumber); + fprintf(out_logfile, "\t%s=%s\n", "api", test->api); + fprintf(out_logfile, "\t%s=%s\n", "platform", test->platform); + fprintf(out_logfile, "\t%s=%s\n", "testFileName", test->testFileName); + fprintf(out_logfile, "\t%s=%s\n", "hintsFileName", test->hintsFileName); + fprintf(out_logfile, "\t%s=%d\n", "deadlineForStonewall", test->deadlineForStonewalling); - fprintf(stdout, "\t%s=%d\n", "stoneWallingWearOut", test->stoneWallingWearOut); - fprintf(stdout, "\t%s=%d\n", "maxTimeDuration", test->maxTimeDuration); - fprintf(stdout, "\t%s=%d\n", "outlierThreshold", + fprintf(out_logfile, "\t%s=%d\n", "stoneWallingWearOut", test->stoneWallingWearOut); + fprintf(out_logfile, "\t%s=%d\n", "maxTimeDuration", test->maxTimeDuration); + fprintf(out_logfile, "\t%s=%d\n", "outlierThreshold", test->outlierThreshold); - fprintf(stdout, "\t%s=%s\n", "options", test->options); - fprintf(stdout, "\t%s=%d\n", "nodes", test->nodes); - fprintf(stdout, "\t%s=%lu\n", "memoryPerTask", (unsigned long) test->memoryPerTask); - fprintf(stdout, "\t%s=%lu\n", "memoryPerNode", (unsigned long) test->memoryPerNode); - fprintf(stdout, "\t%s=%d\n", "tasksPerNode", tasksPerNode); - fprintf(stdout, "\t%s=%d\n", "repetitions", test->repetitions); - fprintf(stdout, "\t%s=%d\n", "multiFile", test->multiFile); - fprintf(stdout, "\t%s=%d\n", "interTestDelay", test->interTestDelay); - fprintf(stdout, "\t%s=%d\n", "fsync", test->fsync); - fprintf(stdout, "\t%s=%d\n", "fsYncperwrite", test->fsyncPerWrite); - fprintf(stdout, "\t%s=%d\n", "useExistingTestFile", + fprintf(out_logfile, "\t%s=%s\n", "options", test->options); + fprintf(out_logfile, "\t%s=%d\n", "nodes", test->nodes); + fprintf(out_logfile, "\t%s=%lu\n", "memoryPerTask", (unsigned long) test->memoryPerTask); + fprintf(out_logfile, "\t%s=%lu\n", "memoryPerNode", (unsigned long) test->memoryPerNode); + fprintf(out_logfile, "\t%s=%d\n", "tasksPerNode", tasksPerNode); + fprintf(out_logfile, "\t%s=%d\n", "repetitions", test->repetitions); + fprintf(out_logfile, "\t%s=%d\n", "multiFile", test->multiFile); + fprintf(out_logfile, "\t%s=%d\n", "interTestDelay", test->interTestDelay); + fprintf(out_logfile, "\t%s=%d\n", "fsync", test->fsync); + fprintf(out_logfile, "\t%s=%d\n", "fsYncperwrite", test->fsyncPerWrite); + fprintf(out_logfile, "\t%s=%d\n", "useExistingTestFile", test->useExistingTestFile); - fprintf(stdout, "\t%s=%d\n", "showHints", test->showHints); - fprintf(stdout, "\t%s=%d\n", "uniqueDir", test->uniqueDir); - fprintf(stdout, "\t%s=%d\n", "showHelp", test->showHelp); - fprintf(stdout, "\t%s=%d\n", "individualDataSets", + fprintf(out_logfile, "\t%s=%d\n", "showHints", test->showHints); + fprintf(out_logfile, "\t%s=%d\n", "uniqueDir", test->uniqueDir); + fprintf(out_logfile, "\t%s=%d\n", "showHelp", test->showHelp); + fprintf(out_logfile, "\t%s=%d\n", "individualDataSets", test->individualDataSets); - fprintf(stdout, "\t%s=%d\n", "singleXferAttempt", + fprintf(out_logfile, "\t%s=%d\n", "singleXferAttempt", test->singleXferAttempt); - fprintf(stdout, "\t%s=%d\n", "readFile", test->readFile); - fprintf(stdout, "\t%s=%d\n", "writeFile", test->writeFile); - fprintf(stdout, "\t%s=%d\n", "filePerProc", test->filePerProc); - fprintf(stdout, "\t%s=%d\n", "reorderTasks", test->reorderTasks); - fprintf(stdout, "\t%s=%d\n", "reorderTasksRandom", + fprintf(out_logfile, "\t%s=%d\n", "readFile", test->readFile); + fprintf(out_logfile, "\t%s=%d\n", "writeFile", test->writeFile); + fprintf(out_logfile, "\t%s=%d\n", "filePerProc", test->filePerProc); + fprintf(out_logfile, "\t%s=%d\n", "reorderTasks", test->reorderTasks); + fprintf(out_logfile, "\t%s=%d\n", "reorderTasksRandom", test->reorderTasksRandom); - fprintf(stdout, "\t%s=%d\n", "reorderTasksRandomSeed", + fprintf(out_logfile, "\t%s=%d\n", "reorderTasksRandomSeed", test->reorderTasksRandomSeed); - fprintf(stdout, "\t%s=%d\n", "randomOffset", test->randomOffset); - fprintf(stdout, "\t%s=%d\n", "checkWrite", test->checkWrite); - fprintf(stdout, "\t%s=%d\n", "checkRead", test->checkRead); - fprintf(stdout, "\t%s=%d\n", "preallocate", test->preallocate); - fprintf(stdout, "\t%s=%d\n", "useFileView", test->useFileView); - fprintf(stdout, "\t%s=%lld\n", "setAlignment", test->setAlignment); - fprintf(stdout, "\t%s=%d\n", "storeFileOffset", test->storeFileOffset); - fprintf(stdout, "\t%s=%d\n", "useSharedFilePointer", + fprintf(out_logfile, "\t%s=%d\n", "randomOffset", test->randomOffset); + fprintf(out_logfile, "\t%s=%d\n", "checkWrite", test->checkWrite); + fprintf(out_logfile, "\t%s=%d\n", "checkRead", test->checkRead); + fprintf(out_logfile, "\t%s=%d\n", "preallocate", test->preallocate); + fprintf(out_logfile, "\t%s=%d\n", "useFileView", test->useFileView); + fprintf(out_logfile, "\t%s=%lld\n", "setAlignment", test->setAlignment); + fprintf(out_logfile, "\t%s=%d\n", "storeFileOffset", test->storeFileOffset); + fprintf(out_logfile, "\t%s=%d\n", "useSharedFilePointer", test->useSharedFilePointer); - fprintf(stdout, "\t%s=%d\n", "useO_DIRECT", test->useO_DIRECT); - fprintf(stdout, "\t%s=%d\n", "useStridedDatatype", + fprintf(out_logfile, "\t%s=%d\n", "useO_DIRECT", test->useO_DIRECT); + fprintf(out_logfile, "\t%s=%d\n", "useStridedDatatype", test->useStridedDatatype); - fprintf(stdout, "\t%s=%d\n", "keepFile", test->keepFile); - fprintf(stdout, "\t%s=%d\n", "keepFileWithError", + fprintf(out_logfile, "\t%s=%d\n", "keepFile", test->keepFile); + fprintf(out_logfile, "\t%s=%d\n", "keepFileWithError", test->keepFileWithError); - fprintf(stdout, "\t%s=%d\n", "quitOnError", test->quitOnError); - fprintf(stdout, "\t%s=%d\n", "verbose", verbose); - fprintf(stdout, "\t%s=%s\n", "data packet type", data_packets[test->dataPacketType]); - fprintf(stdout, "\t%s=%d\n", "setTimeStampSignature/incompressibleSeed", + fprintf(out_logfile, "\t%s=%d\n", "quitOnError", test->quitOnError); + fprintf(out_logfile, "\t%s=%d\n", "verbose", verbose); + fprintf(out_logfile, "\t%s=%s\n", "data packet type", data_packets[test->dataPacketType]); + fprintf(out_logfile, "\t%s=%d\n", "setTimeStampSignature/incompressibleSeed", test->setTimeStampSignature); /* Seed value was copied into setTimeStampSignature as well */ - fprintf(stdout, "\t%s=%d\n", "collective", test->collective); - fprintf(stdout, "\t%s=%lld", "segmentCount", test->segmentCount); + fprintf(out_logfile, "\t%s=%d\n", "collective", test->collective); + fprintf(out_logfile, "\t%s=%lld", "segmentCount", test->segmentCount); #ifdef HAVE_GPFS_FCNTL_H - fprintf(stdout, "\t%s=%d\n", "gpfsHintAccess", test->gpfs_hint_access); - fprintf(stdout, "\t%s=%d\n", "gpfsReleaseToken", test->gpfs_release_token); + fprintf(out_logfile, "\t%s=%d\n", "gpfsHintAccess", test->gpfs_hint_access); + fprintf(out_logfile, "\t%s=%d\n", "gpfsReleaseToken", test->gpfs_release_token); #endif if (strcasecmp(test->api, "HDF5") == 0) { - fprintf(stdout, " (datasets)"); + fprintf(out_logfile, " (datasets)"); } - fprintf(stdout, "\n"); - fprintf(stdout, "\t%s=%lld\n", "transferSize", test->transferSize); - fprintf(stdout, "\t%s=%lld\n", "blockSize", test->blockSize); + fprintf(out_logfile, "\n"); + fprintf(out_logfile, "\t%s=%lld\n", "transferSize", test->transferSize); + fprintf(out_logfile, "\t%s=%lld\n", "blockSize", test->blockSize); } static double mean_of_array_of_doubles(double *values, int len) @@ -1716,34 +1759,34 @@ static void PrintLongSummaryOneOperation(IOR_test_t *test, double *times, char * ops = ops_values(reps, results->aggFileSizeForBW, params->transferSize, times); - fprintf(stdout, "%-9s ", operation); - fprintf(stdout, "%10.2f ", bw->max / MEBIBYTE); - fprintf(stdout, "%10.2f ", bw->min / MEBIBYTE); - fprintf(stdout, "%10.2f ", bw->mean / MEBIBYTE); - fprintf(stdout, "%10.2f ", bw->sd / MEBIBYTE); - fprintf(stdout, "%10.2f ", ops->max); - fprintf(stdout, "%10.2f ", ops->min); - fprintf(stdout, "%10.2f ", ops->mean); - fprintf(stdout, "%10.2f ", ops->sd); - fprintf(stdout, "%10.5f ", + fprintf(out_logfile, "%-9s ", operation); + fprintf(out_logfile, "%10.2f ", bw->max / MEBIBYTE); + fprintf(out_logfile, "%10.2f ", bw->min / MEBIBYTE); + fprintf(out_logfile, "%10.2f ", bw->mean / MEBIBYTE); + fprintf(out_logfile, "%10.2f ", bw->sd / MEBIBYTE); + fprintf(out_logfile, "%10.2f ", ops->max); + fprintf(out_logfile, "%10.2f ", ops->min); + fprintf(out_logfile, "%10.2f ", ops->mean); + fprintf(out_logfile, "%10.2f ", ops->sd); + fprintf(out_logfile, "%10.5f ", mean_of_array_of_doubles(times, reps)); - fprintf(stdout, "%d ", params->id); - fprintf(stdout, "%d ", params->numTasks); - fprintf(stdout, "%d ", params->tasksPerNode); - fprintf(stdout, "%d ", params->repetitions); - fprintf(stdout, "%d ", params->filePerProc); - fprintf(stdout, "%d ", params->reorderTasks); - fprintf(stdout, "%d ", params->taskPerNodeOffset); - fprintf(stdout, "%d ", params->reorderTasksRandom); - fprintf(stdout, "%d ", params->reorderTasksRandomSeed); - fprintf(stdout, "%lld ", params->segmentCount); - fprintf(stdout, "%lld ", params->blockSize); - fprintf(stdout, "%lld ", params->transferSize); - fprintf(stdout, "%lld ", results->aggFileSizeForBW[0]); - fprintf(stdout, "%s ", params->api); - fprintf(stdout, "%d", params->referenceNumber); - fprintf(stdout, "\n"); - fflush(stdout); + fprintf(out_logfile, "%d ", params->id); + fprintf(out_logfile, "%d ", params->numTasks); + fprintf(out_logfile, "%d ", params->tasksPerNode); + fprintf(out_logfile, "%d ", params->repetitions); + fprintf(out_logfile, "%d ", params->filePerProc); + fprintf(out_logfile, "%d ", params->reorderTasks); + fprintf(out_logfile, "%d ", params->taskPerNodeOffset); + fprintf(out_logfile, "%d ", params->reorderTasksRandom); + fprintf(out_logfile, "%d ", params->reorderTasksRandomSeed); + fprintf(out_logfile, "%lld ", params->segmentCount); + fprintf(out_logfile, "%lld ", params->blockSize); + fprintf(out_logfile, "%lld ", params->transferSize); + fprintf(out_logfile, "%lld ", results->aggFileSizeForBW[0]); + fprintf(out_logfile, "%s ", params->api); + fprintf(out_logfile, "%d", params->referenceNumber); + fprintf(out_logfile, "\n"); + fflush(out_logfile); free(bw); free(ops); @@ -1765,12 +1808,12 @@ static void PrintLongSummaryHeader() if (rank != 0 || verbose < VERBOSE_0) return; - fprintf(stdout, "\n"); - fprintf(stdout, "%-9s %10s %10s %10s %10s %10s %10s %10s %10s %10s", + fprintf(out_logfile, "\n"); + fprintf(out_logfile, "%-9s %10s %10s %10s %10s %10s %10s %10s %10s %10s", "Operation", "Max(MiB)", "Min(MiB)", "Mean(MiB)", "StdDev", "Max(OPs)", "Min(OPs)", "Mean(OPs)", "StdDev", "Mean(s)"); - fprintf(stdout, " Test# #Tasks tPN reps fPP reord reordoff reordrand seed" + fprintf(out_logfile, " Test# #Tasks tPN reps fPP reord reordoff reordrand seed" " segcnt blksiz xsize aggsize API RefNum\n"); } @@ -1781,8 +1824,8 @@ static void PrintLongSummaryAllTests(IOR_test_t *tests_head) if (rank != 0 || verbose < VERBOSE_0) return; - fprintf(stdout, "\n"); - fprintf(stdout, "Summary of all tests:"); + fprintf(out_logfile, "\n"); + fprintf(out_logfile, "Summary of all tests:"); PrintLongSummaryHeader(); for (tptr = tests_head; tptr != NULL; tptr = tptr->next) { @@ -1814,13 +1857,13 @@ static void PrintShortSummary(IOR_test_t * test) max_read = MAX(bw, max_read); } - fprintf(stdout, "\n"); + fprintf(out_logfile, "\n"); if (params->writeFile) { - fprintf(stdout, "Max Write: %.2f MiB/sec (%.2f MB/sec)\n", + fprintf(out_logfile, "Max Write: %.2f MiB/sec (%.2f MB/sec)\n", max_write/MEBIBYTE, max_write/MEGABYTE); } if (params->readFile) { - fprintf(stdout, "Max Read: %.2f MiB/sec (%.2f MB/sec)\n", + fprintf(out_logfile, "Max Read: %.2f MiB/sec (%.2f MB/sec)\n", max_read/MEBIBYTE, max_read/MEGABYTE); } } @@ -1865,7 +1908,7 @@ static void file_hits_histogram(IOR_param_t *params) } MPI_CHECK(MPI_Gather(&rankOffset, 1, MPI_INT, rankoffs, - 1, MPI_INT, 0, MPI_COMM_WORLD), + 1, MPI_INT, 0, mpi_comm_world), "MPI_Gather error"); if (rank != 0) @@ -1881,14 +1924,14 @@ static void file_hits_histogram(IOR_param_t *params) if (ifile == filecont[jfile]) filehits[ifile]++; } - fprintf(stdout, "#File Hits Dist:"); + fprintf(out_logfile, "#File Hits Dist:"); jfile = 0; ifile = 0; while (jfile < params->numTasks && ifile < params->numTasks) { - fprintf(stdout, " %d", filehits[ifile]); + fprintf(out_logfile, " %d", filehits[ifile]); jfile += filehits[ifile], ifile++; } - fprintf(stdout, "\n"); + fprintf(out_logfile, "\n"); free(rankoffs); free(filecont); free(filehits); @@ -1919,7 +1962,7 @@ static void *HogMemory(IOR_param_t *params) size = params->memoryPerTask; } else if (params->memoryPerNode != 0) { if (verbose >= VERBOSE_3) - fprintf(stderr, "This node hogging %ld bytes of memory\n", + fprintf(out_logfile, "This node hogging %ld bytes of memory\n", params->memoryPerNode); size = params->memoryPerNode / params->tasksPerNode; } else { @@ -1927,7 +1970,7 @@ static void *HogMemory(IOR_param_t *params) } if (verbose >= VERBOSE_3) - fprintf(stderr, "This task hogging %ld bytes of memory\n", size); + fprintf(out_logfile, "This task hogging %ld bytes of memory\n", size); buf = malloc_and_touch(size); if (buf == NULL) @@ -1958,39 +2001,39 @@ static void TestIoSys(IOR_test_t *test) /* set up communicator for test */ if (params->numTasks > numTasksWorld) { if (rank == 0) { - fprintf(stdout, + fprintf(out_logfile, "WARNING: More tasks requested (%d) than available (%d),", params->numTasks, numTasksWorld); - fprintf(stdout, " running on %d tasks.\n", + fprintf(out_logfile, " running on %d tasks.\n", numTasksWorld); } params->numTasks = numTasksWorld; } - MPI_CHECK(MPI_Comm_group(MPI_COMM_WORLD, &orig_group), + MPI_CHECK(MPI_Comm_group(mpi_comm_world, &orig_group), "MPI_Comm_group() error"); range[0] = 0; /* first rank */ range[1] = params->numTasks - 1; /* last rank */ range[2] = 1; /* stride */ MPI_CHECK(MPI_Group_range_incl(orig_group, 1, &range, &new_group), "MPI_Group_range_incl() error"); - MPI_CHECK(MPI_Comm_create(MPI_COMM_WORLD, new_group, &testComm), + MPI_CHECK(MPI_Comm_create(mpi_comm_world, new_group, &testComm), "MPI_Comm_create() error"); MPI_CHECK(MPI_Group_free(&orig_group), "MPI_Group_Free() error"); MPI_CHECK(MPI_Group_free(&new_group), "MPI_Group_Free() error"); params->testComm = testComm; if (testComm == MPI_COMM_NULL) { /* tasks not in the group do not participate in this test */ - MPI_CHECK(MPI_Barrier(MPI_COMM_WORLD), "barrier error"); + MPI_CHECK(MPI_Barrier(mpi_comm_world), "barrier error"); return; } if (rank == 0 && verbose >= VERBOSE_1) { - fprintf(stdout, "Participating tasks: %d\n", params->numTasks); - fflush(stdout); + fprintf(out_logfile, "Participating tasks: %d\n", params->numTasks); + fflush(out_logfile); } if (rank == 0 && params->reorderTasks == TRUE && verbose >= VERBOSE_1) { - fprintf(stdout, + fprintf(out_logfile, "Using reorderTasks '-C' (expecting block, not cyclic, task assignment)\n"); - fflush(stdout); + fflush(out_logfile); } params->tasksPerNode = CountTasksPerNode(params->numTasks, testComm); @@ -2037,16 +2080,16 @@ static void TestIoSys(IOR_test_t *test) params->timeStampSignatureValue = (unsigned int)currentTime; if (verbose >= VERBOSE_2) { - fprintf(stdout, + fprintf(out_logfile, "Using Time Stamp %u (0x%x) for Data Signature\n", params->timeStampSignatureValue, params->timeStampSignatureValue); } } if (rep == 0 && verbose >= VERBOSE_0) { - fprintf(stdout, "\n"); - fprintf(stdout, "access bw(MiB/s) block(KiB) xfer(KiB) open(s) wr/rd(s) close(s) total(s) iter\n"); - fprintf(stdout, "------ --------- ---------- --------- -------- -------- -------- -------- ----\n"); + fprintf(out_logfile, "\n"); + fprintf(out_logfile, "access bw(MiB/s) block(KiB) xfer(KiB) open(s) wr/rd(s) close(s) total(s) iter\n"); + fprintf(out_logfile, "------ --------- ---------- --------- -------- -------- -------- -------- ----\n"); } } MPI_CHECK(MPI_Bcast @@ -2065,7 +2108,7 @@ static void TestIoSys(IOR_test_t *test) if (params->writeFile && !test_time_elapsed(params, startTime)) { GetTestFileName(testFileName, params); if (verbose >= VERBOSE_3) { - fprintf(stdout, "task %d writing %s\n", rank, + fprintf(out_logfile, "task %d writing %s\n", rank, testFileName); } DelaySecs(params->interTestDelay); @@ -2082,15 +2125,15 @@ static void TestIoSys(IOR_test_t *test) MPI_CHECK(MPI_Barrier(testComm), "barrier error"); if (rank == 0 && verbose >= VERBOSE_1) { - fprintf(stderr, + fprintf(out_logfile, "Commencing write performance test: %s", CurrentTimeString()); } timer[2][rep] = GetTimeStamp(); dataMoved = WriteOrRead(params, results, fd, WRITE, &ioBuffers); if (params->verbose >= VERBOSE_4) { - printf("* data moved = %llu\n", dataMoved); - fflush(stdout); + fprintf(out_logfile, "* data moved = %llu\n", dataMoved); + fflush(out_logfile); } timer[3][rep] = GetTimeStamp(); if (params->intraTestBarriers) @@ -2125,9 +2168,9 @@ static void TestIoSys(IOR_test_t *test) if (params->checkWrite && !test_time_elapsed(params, startTime)) { MPI_CHECK(MPI_Barrier(testComm), "barrier error"); if (rank == 0 && verbose >= VERBOSE_1) { - fprintf(stdout, + fprintf(out_logfile, "Verifying contents of the file(s) just written.\n"); - fprintf(stdout, "%s\n", CurrentTimeString()); + fprintf(out_logfile, "%s\n", CurrentTimeString()); } if (params->reorderTasks) { /* move two nodes away from writing node */ @@ -2197,7 +2240,7 @@ static void TestIoSys(IOR_test_t *test) GetTestFileName(testFileName, params); if (verbose >= VERBOSE_3) { - fprintf(stdout, "task %d reading %s\n", rank, + fprintf(out_logfile, "task %d reading %s\n", rank, testFileName); } DelaySecs(params->interTestDelay); @@ -2210,7 +2253,7 @@ static void TestIoSys(IOR_test_t *test) MPI_CHECK(MPI_Barrier(testComm), "barrier error"); if (rank == 0 && verbose >= VERBOSE_1) { - fprintf(stderr, + fprintf(out_logfile, "Commencing read performance test: %s", CurrentTimeString()); } @@ -2275,7 +2318,7 @@ static void TestIoSys(IOR_test_t *test) } /* Sync with the tasks that did not participate in this test */ - MPI_CHECK(MPI_Barrier(MPI_COMM_WORLD), "barrier error"); + MPI_CHECK(MPI_Barrier(mpi_comm_world), "barrier error"); } @@ -2495,7 +2538,7 @@ static IOR_offset_t *GetOffsetArraySequential(IOR_param_t * test, } /** - * Returns a precomputed array of IOR_offsett_t for the inner benchmark loop. + * Returns a precomputed array of IOR_offset_t for the inner benchmark loop. * They get created sequentially and mixed up in the end. The last array element * is set to -1 as end marker. * It should be noted that as the seeds get synchronised across all processes @@ -2642,7 +2685,7 @@ static IOR_offset_t WriteOrRead(IOR_param_t * test, IOR_results_t * results, voi int errors = 0; IOR_offset_t amtXferred; IOR_offset_t transferCount = 0; - IOR_offset_t pairCnt = 0; + uint64_t pairCnt = 0; IOR_offset_t *offsetArray; int pretendRank; IOR_offset_t dataMoved = 0; /* for data rate calculation */ @@ -2675,10 +2718,31 @@ static IOR_offset_t WriteOrRead(IOR_param_t * test, IOR_results_t * results, voi > test->deadlineForStonewalling)) || (test->stoneWallingWearOutIterations != 0 && pairCnt == test->stoneWallingWearOutIterations) ; } if (test->stoneWallingWearOut){ + if (verbose >= VERBOSE_1){ + fprintf(out_logfile, "%d: stonewalling pairs accessed: %lld\n", rank, (long long) pairCnt); + } + long long data_moved_ll = (long long) dataMoved; + long long pairs_accessed_min = 0; MPI_CHECK(MPI_Allreduce(& pairCnt, &results->pairs_accessed, 1, MPI_LONG_LONG_INT, MPI_MAX, testComm), "cannot reduce pairs moved"); - if (verbose >= VERBOSE_1){ - printf("%d: stonewalling pairs accessed globally: %lld this rank: %lld\n", rank, (long long) results->pairs_accessed, (long long) pairCnt); + double stonewall_runtime = GetTimeStamp() - startForStonewall; + results->stonewall_time = stonewall_runtime; + MPI_CHECK(MPI_Reduce(& pairCnt, & pairs_accessed_min, + 1, MPI_LONG_LONG_INT, MPI_MIN, 0, testComm), "cannot reduce pairs moved"); + MPI_CHECK(MPI_Reduce(& data_moved_ll, & results->stonewall_min_data_accessed, + 1, MPI_LONG_LONG_INT, MPI_MIN, 0, testComm), "cannot reduce pairs moved"); + MPI_CHECK(MPI_Reduce(& data_moved_ll, & results->stonewall_avg_data_accessed, + 1, MPI_LONG_LONG_INT, MPI_SUM, 0, testComm), "cannot reduce pairs moved"); + + if(rank == 0){ + fprintf(out_logfile, "stonewalling pairs accessed min: %lld max: %zu -- min data: %.1f GiB mean data: %.1f GiB time: %.1fs\n", + pairs_accessed_min, results->pairs_accessed, + results->stonewall_min_data_accessed /1024.0 / 1024 / 1024, results->stonewall_avg_data_accessed / 1024.0 / 1024 / 1024 / test->numTasks , results->stonewall_time); + results->stonewall_min_data_accessed *= test->numTasks; + } + if(pairs_accessed_min == pairCnt){ + results->stonewall_min_data_accessed = 0; + results->stonewall_avg_data_accessed = 0; } if(pairCnt != results->pairs_accessed){ // some work needs still to be done ! @@ -2709,7 +2773,7 @@ WriteTimes(IOR_param_t * test, double **timer, int iteration, int writeOrRead) { char accessType[MAX_STR]; char timerName[MAX_STR]; - int i, start, stop; + int i, start = 0, stop = 0; if (writeOrRead == WRITE) { start = 0; @@ -2765,7 +2829,7 @@ WriteTimes(IOR_param_t * test, double **timer, int iteration, int writeOrRead) strcpy(timerName, "invalid timer"); break; } - fprintf(stdout, "Test %d: Iter=%d, Task=%d, Time=%f, %s\n", + fprintf(out_logfile, "Test %d: Iter=%d, Task=%d, Time=%f, %s\n", test->id, iteration, (int)rank, timer[i][iteration], timerName); } diff --git a/src/ior.h b/src/ior.h index e01f29b..ec386d7 100755 --- a/src/ior.h +++ b/src/ior.h @@ -245,4 +245,7 @@ int CountTasksPerNode(int numTasks, MPI_Comm comm); */ IOR_test_t * ior_run(int argc, char **argv, MPI_Comm world_com, FILE * out_logfile); +/* Actual IOR Main function, renamed to allow library usage */ +int ior_main(int argc, char **argv); + #endif /* !_IOR_H */ diff --git a/src/iordef.h b/src/iordef.h index 8a76d3d..e1784bd 100755 --- a/src/iordef.h +++ b/src/iordef.h @@ -32,7 +32,7 @@ # include # include -# define MAXPATHLEN 1024 +# define MAXPATHLEN 2048 # define F_OK 00 # define W_OK 02 # define R_OK 04 diff --git a/testing/basic-tests.sh b/testing/basic-tests.sh index 8615df8..00978e9 100755 --- a/testing/basic-tests.sh +++ b/testing/basic-tests.sh @@ -7,7 +7,7 @@ # Example: export IOR_EXTRA="-v -v -v" IOR_MPIRUN=${IOR_MPIRUN:-mpiexec -np} -IOR_BIN_DIR=${IOR_EXEC:-./build/src} +IOR_BIN_DIR=${IOR_BIN_DIR:-./build/src} IOR_OUT=${IOR_OUT:-./build/test} IOR_EXTRA=${IOR_EXTRA:-} # Add global options like verbosity MDTEST_EXTRA=${MDTEST_EXTRA:-} From e31b2dcec1f6feeda0933837680d908a64ff7c8d Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 11:57:04 +0100 Subject: [PATCH 19/80] Added missing file. --- src/ior-main.c | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/ior-main.c diff --git a/src/ior-main.c b/src/ior-main.c new file mode 100644 index 0000000..7fc567f --- /dev/null +++ b/src/ior-main.c @@ -0,0 +1,6 @@ +#include "ior.h" + +int main(int argc, char **argv) +{ + return ior_main(argc, argv); +} From 231ebe96706af0e89571ac69bded4f8a0ca314c9 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 11:57:10 +0100 Subject: [PATCH 20/80] Split test. --- testing/basic-tests.sh | 80 ++----------------------------------- testing/bug-multi-node.conf | 18 +++++++++ testing/complex-tests.sh | 25 ++++++++++++ testing/test-lib.sh | 74 ++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 76 deletions(-) create mode 100644 testing/bug-multi-node.conf create mode 100755 testing/complex-tests.sh create mode 100644 testing/test-lib.sh diff --git a/testing/basic-tests.sh b/testing/basic-tests.sh index 00978e9..e53c091 100755 --- a/testing/basic-tests.sh +++ b/testing/basic-tests.sh @@ -6,65 +6,9 @@ # You can override the defaults by setting the variables before invoking the script, or simply set them here... # Example: export IOR_EXTRA="-v -v -v" -IOR_MPIRUN=${IOR_MPIRUN:-mpiexec -np} -IOR_BIN_DIR=${IOR_BIN_DIR:-./build/src} -IOR_OUT=${IOR_OUT:-./build/test} -IOR_EXTRA=${IOR_EXTRA:-} # Add global options like verbosity -MDTEST_EXTRA=${MDTEST_EXTRA:-} +ROOT=${0%/*} -################################################################################ -mkdir -p ${IOR_OUT} - -## Sanity check - -if [[ ! -e ${IOR_OUT} ]]; then - echo "Could not create output dir ${IOR_OUT}" - exit 1 -fi - -if [[ ! -e ${IOR_BIN_DIR}/ior ]]; then - echo "IOR Executable \"${IOR_BIN_DIR}/ior\" does not exist! Call me from the root directory!" - exit 1 -fi - -if [[ ! -e ${IOR_BIN_DIR}/mdtest ]]; then - echo "MDTest Executable \"${IOR_BIN_DIR}/mdtest\" does not exist! Call me from the root directory!" - exit 1 -fi - - - -ERRORS=0 # Number of errors detected while running -I=0 -function IOR(){ - RANKS=$1 - shift - WHAT="${IOR_MPIRUN} $RANKS ${IOR_BIN_DIR}/ior ${@} ${IOR_EXTRA} -o /dev/shm/ior" - $WHAT 1>${IOR_OUT}/$I 2>&1 - if [[ $? != 0 ]]; then - echo -n "ERR" - ERRORS=$(($ERRORS + 1)) - else - echo -n "OK " - fi - echo " $WHAT" - I=$((${I}+1)) -} - -function MDTEST(){ - RANKS=$1 - shift - WHAT="${IOR_MPIRUN} $RANKS ${IOR_BIN_DIR}/mdtest ${@} ${MDTEST_EXTRA} -d /dev/shm/ior" - $WHAT 1>${IOR_OUT}/$I 2>&1 - if [[ $? != 0 ]]; then - echo -n "ERR" - ERRORS=$(($ERRORS + 1)) - else - echo -n "OK " - fi - echo " $WHAT" - I=$((${I}+1)) -} +source $ROOT/test-lib.sh MDTEST 1 -a POSIX @@ -72,26 +16,10 @@ IOR 1 -a POSIX -w -z -F -Y -e -i1 -m -t 100k -b 1000k IOR 1 -a POSIX -w -z -F -k -e -i2 -m -t 100k -b 100k IOR 1 -a POSIX -r -z -F -k -e -i1 -m -t 100k -b 100k -IOR 2 -a POSIX -w -z -C -F -k -e -i1 -m -t 100k -b 100k +IOR 2 -a POSIX -w -z -C -F -k -e -i1 -m -t 100k -b 100k IOR 2 -a POSIX -w -z -C -Q 1 -F -k -e -i1 -m -t 100k -b 100k IOR 2 -a POSIX -r -z -Z -Q 2 -F -k -e -i1 -m -t 100k -b 100k IOR 2 -a POSIX -r -z -Z -Q 3 -X 13 -F -k -e -i1 -m -t 100k -b 100k IOR 2 -a POSIX -w -z -Z -Q 1 -X -13 -F -e -i1 -m -t 100k -b 100k -#shared tests -IOR 2 -a POSIX -w -z -Y -e -i1 -m -t 100k -b 100k -IOR 2 -a POSIX -w -k -e -i1 -m -t 100k -b 100k -IOR 2 -a POSIX -r -z -k -e -i1 -m -t 100k -b 100k - -#test mutually exclusive options -IOR 2 -a POSIX -w -z -k -e -i1 -m -t 100k -b 100k -IOR 2 -a POSIX -w -z - -k -e -i1 -m -t 100k -b 100k -IOR 2 -a POSIX -w -Z -i1 -m -t 100k -b 100k -d 0.1 - -if [[ ${ERRORS} == 0 ]] ; then - echo "PASSED" -else - echo "Error, check the output files!" -fi - -exit ${ERRORS} +END diff --git a/testing/bug-multi-node.conf b/testing/bug-multi-node.conf new file mode 100644 index 0000000..1f79715 --- /dev/null +++ b/testing/bug-multi-node.conf @@ -0,0 +1,18 @@ +IOR START +reorderTasksConstant=1 +repetitions=1 +segmentCount=1 +verbose=0 +fsync=0 +checkWrite=1 +blockSize=16 +setTimeStampSignature=1511817315 +checkRead=0 +readFile=1 +filePerProc=0 +writeFile=1 +transferSize=16 +intraTestBarriers=0 + +RUN +IOR STOP diff --git a/testing/complex-tests.sh b/testing/complex-tests.sh new file mode 100755 index 0000000..665727b --- /dev/null +++ b/testing/complex-tests.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Test script for more complex IOR functionality testing various patterns +# You can override the defaults by setting the variables before invoking the script, or simply set them here... +# Example: export IOR_EXTRA="-v -v -v" + +ROOT=${0%/*} + +source $ROOT/test-lib.sh + +#shared tests +IOR 2 -a POSIX -w -z -Y -e -i1 -m -t 100k -b 100k +IOR 2 -a POSIX -w -k -e -i1 -m -t 100k -b 100k +IOR 2 -a POSIX -r -z-k -e -i1 -m -t 100k -b 100k + +#test mutually exclusive options +IOR 2 -a POSIX -w -z -k -e -i1 -m -t 100k -b 100k +IOR 2 -a POSIX -w -z -k -e -i1 -m -t 100k -b 100k +IOR 2 -a POSIX -w -Z -i1 -m -t 100k -b 100k -d 0.1 + +# Now set the num tasks per node to 1: +export IOR_FAKE_TASK_PER_NODES=1 +IOR 2 -a POSIX -f $ROOT/bug-multi-node.conf + +END diff --git a/testing/test-lib.sh b/testing/test-lib.sh new file mode 100644 index 0000000..54ab0b2 --- /dev/null +++ b/testing/test-lib.sh @@ -0,0 +1,74 @@ +# Test script for basic IOR functionality testing various patterns +# It is kept as simple as possible and outputs the parameters used such that any test can be rerun easily. + +# You can override the defaults by setting the variables before invoking the script, or simply set them here... +# Example: export IOR_EXTRA="-v -v -v" + +IOR_MPIRUN=${IOR_MPIRUN:-mpiexec -np} +IOR_BIN_DIR=${IOR_BIN_DIR:-./build/src} +IOR_OUT=${IOR_OUT:-./build/test} +IOR_EXTRA=${IOR_EXTRA:-} # Add global options like verbosity +MDTEST_EXTRA=${MDTEST_EXTRA:-} + +################################################################################ +mkdir -p ${IOR_OUT} +mkdir -p /dev/shm/mdest + +## Sanity check + +if [[ ! -e ${IOR_OUT} ]]; then + echo "Could not create output dir ${IOR_OUT}" + exit 1 +fi + +if [[ ! -e ${IOR_BIN_DIR}/ior ]]; then + echo "IOR Executable \"${IOR_BIN_DIR}/ior\" does not exist! Call me from the root directory!" + exit 1 +fi + +if [[ ! -e ${IOR_BIN_DIR}/mdtest ]]; then + echo "MDTest Executable \"${IOR_BIN_DIR}/mdtest\" does not exist! Call me from the root directory!" + exit 1 +fi + +ERRORS=0 # Number of errors detected while running +I=0 +function IOR(){ + RANKS=$1 + shift + WHAT="${IOR_MPIRUN} $RANKS ${IOR_BIN_DIR}/ior ${@} ${IOR_EXTRA} -o /dev/shm/ior" + $WHAT 1>${IOR_OUT}/$I 2>&1 + if [[ $? != 0 ]]; then + echo -n "ERR" + ERRORS=$(($ERRORS + 1)) + else + echo -n "OK " + fi + echo " $WHAT" + I=$((${I}+1)) +} + +function MDTEST(){ + RANKS=$1 + shift + WHAT="${IOR_MPIRUN} $RANKS ${IOR_BIN_DIR}/mdtest ${@} ${MDTEST_EXTRA} -d /dev/shm/mdest" + $WHAT 1>${IOR_OUT}/$I 2>&1 + if [[ $? != 0 ]]; then + echo -n "ERR" + ERRORS=$(($ERRORS + 1)) + else + echo -n "OK " + fi + echo " $WHAT" + I=$((${I}+1)) +} + +function END(){ + if [[ ${ERRORS} == 0 ]] ; then + echo "PASSED" + else + echo "Error, check the output files!" + fi + + exit ${ERRORS} +} From e10bebe653bae02ca466da9072b6e7025b61124f Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 12:14:34 +0100 Subject: [PATCH 21/80] MDTest supports only POSIX at the moment. Added command line check. --- src/ior.c | 1 - src/mdtest.c | 4 ++++ testing/basic-tests.sh | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ior.c b/src/ior.c index f4dbe41..59b7802 100755 --- a/src/ior.c +++ b/src/ior.c @@ -2701,7 +2701,6 @@ static IOR_offset_t WriteOrRead(IOR_param_t * test, IOR_results_t * results, voi offsetArray = GetOffsetArraySequential(test, pretendRank); } - /* check for stonewall */ startForStonewall = GetTimeStamp(); hitStonewall = ((test->deadlineForStonewalling != 0) diff --git a/src/mdtest.c b/src/mdtest.c index defd6b6..838a586 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -1646,6 +1646,10 @@ void valid_tests() { FAIL("-c not compatible with -B"); } + if ( strcasecmp(backend_name, "POSIX") != 0) { + FAIL("-a only supported interface is POSIX right now!"); + } + /* check for shared file incompatibilities */ if (unique_dir_per_task && shared_file && rank == 0) { FAIL("-u not compatible with -S"); diff --git a/testing/basic-tests.sh b/testing/basic-tests.sh index e53c091..01d7477 100755 --- a/testing/basic-tests.sh +++ b/testing/basic-tests.sh @@ -11,6 +11,7 @@ ROOT=${0%/*} source $ROOT/test-lib.sh MDTEST 1 -a POSIX +#MDTEST 2 -a POSIX -W 2 IOR 1 -a POSIX -w -z -F -Y -e -i1 -m -t 100k -b 1000k IOR 1 -a POSIX -w -z -F -k -e -i2 -m -t 100k -b 100k From 15c383b0d1b89c14e3122fc053fea9d521da65b8 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 12:20:29 +0100 Subject: [PATCH 22/80] Extended description for #44 --- src/ior.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ior.c b/src/ior.c index 59b7802..6a2a153 100755 --- a/src/ior.c +++ b/src/ior.c @@ -826,7 +826,7 @@ static void DisplayUsage(char **argv) " -s N segmentCount -- number of segments", " -S useStridedDatatype -- put strided access into datatype [not working]", " -t N transferSize -- size of transfer in bytes (e.g.: 8, 4k, 2m, 1g)", - " -T N maxTimeDuration -- max time in minutes for each test", + " -T N maxTimeDuration -- max time in minutes executing repeated test; it aborts only between iterations and not within a test!", " -u uniqueDir -- use unique directory name for each file-per-process", " -U S hintsFileName -- full name for hints file", " -v verbose -- output information (repeating flag increases level)", From 378789737f605241eccd81594706c78a2bb3cf22 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 13:24:59 +0100 Subject: [PATCH 23/80] Dummy AIORI driver --- src/Makefile.am | 2 +- src/aiori-DUMMY.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++ src/aiori.c | 3 ++- src/aiori.h | 1 + 4 files changed, 70 insertions(+), 2 deletions(-) create mode 100755 src/aiori-DUMMY.c diff --git a/src/Makefile.am b/src/Makefile.am index 872a5a8..4bc3068 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,7 +5,7 @@ endif noinst_HEADERS = ior.h utilities.h parse_options.h aiori.h iordef.h getopt/optlist.h -extraSOURCES = aiori.c +extraSOURCES = aiori.c aiori-DUMMY.c extraLDADD = extraLDFLAGS = extraCPPFLAGS = diff --git a/src/aiori-DUMMY.c b/src/aiori-DUMMY.c new file mode 100755 index 0000000..3b7daf3 --- /dev/null +++ b/src/aiori-DUMMY.c @@ -0,0 +1,66 @@ +/* +* Dummy implementation doesn't do anything besides waiting +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "ior.h" +#include "aiori.h" +#include "utilities.h" + +static void *DUMMY_Create(char *testFileName, IOR_param_t * param) +{ + return 0; +} + +static void *DUMMY_Open(char *testFileName, IOR_param_t * param) +{ + return 0; +} + +static void DUMMY_Fsync(void *fd, IOR_param_t * param) +{ +} + +static void DUMMY_Close(void *fd, IOR_param_t * param) +{ +} + +static void DUMMY_Delete(char *testFileName, IOR_param_t * param) +{ +} + +static void DUMMY_SetVersion(IOR_param_t * test) +{ + sprintf(test->apiVersion, "DUMMY"); +} + +static IOR_offset_t DUMMY_GetFileSize(IOR_param_t * test, MPI_Comm testComm, char *testFileName) +{ return 0; +} + +static IOR_offset_t DUMMY_Xfer(int access, void *file, IOR_size_t * buffer, IOR_offset_t length, IOR_param_t * param){ + if (rank == 0){ + usleep(100000); + } + return length; +} + + +ior_aiori_t dummy_aiori = { + .name = "DUMMY", + .create = DUMMY_Create, + .open = DUMMY_Open, + .xfer = DUMMY_Xfer, + .close = DUMMY_Close, + .delete = DUMMY_Delete, + .set_version = DUMMY_SetVersion, + .fsync = DUMMY_Fsync, + .get_file_size = DUMMY_GetFileSize, +}; diff --git a/src/aiori.c b/src/aiori.c index 677c1ea..69ed158 100644 --- a/src/aiori.c +++ b/src/aiori.c @@ -28,7 +28,8 @@ */ ior_aiori_t *available_aiori[] = { - + & dummy_aiori, + #ifdef USE_HDF5_AIORI &hdf5_aiori, #endif diff --git a/src/aiori.h b/src/aiori.h index 4ee400a..ee96795 100755 --- a/src/aiori.h +++ b/src/aiori.h @@ -79,6 +79,7 @@ typedef struct ior_aiori { int (*stat) (const char *path, struct stat *buf, IOR_param_t * param); } ior_aiori_t; +extern ior_aiori_t dummy_aiori; extern ior_aiori_t hdf5_aiori; extern ior_aiori_t hdfs_aiori; extern ior_aiori_t mpiio_aiori; From 812b798f05b664e48fbdaefa88ce897f55470e15 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 14:01:11 +0100 Subject: [PATCH 24/80] IOR allows to store actual performaned stonewalling count into a status file. --- src/ior.c | 18 +++++++++++++-- src/ior.h | 2 ++ src/mdtest.c | 36 ++---------------------------- src/parse_options.c | 2 ++ src/utilities.c | 47 ++++++++++++++++++++++++++++++++++++++++ src/utilities.h | 23 ++++++++++++++++++++ testing/complex-tests.sh | 6 +++++ 7 files changed, 98 insertions(+), 36 deletions(-) diff --git a/src/ior.c b/src/ior.c index 6a2a153..4889109 100755 --- a/src/ior.c +++ b/src/ior.c @@ -100,7 +100,11 @@ IOR_test_t * ior_run(int argc, char **argv, MPI_Comm world_com, FILE * world_out TestIoSys(tptr); if(rank == 0 && tptr->params.stoneWallingWearOut){ - fprintf(out_logfile, "Pairs deadlineForStonewallingaccessed: %lld\n", (long long) tptr->results->pairs_accessed); + if (tptr->params.stoneWallingStatusFile){ + StoreStoneWallingIterations(tptr->params.stoneWallingStatusFile, tptr->results->pairs_accessed); + }else{ + fprintf(out_logfile, "Pairs deadlineForStonewallingaccessed: %lld\n", (long long) tptr->results->pairs_accessed); + } } tptr->results->errors = totalErrorCount; } @@ -197,7 +201,9 @@ int ior_main(int argc, char **argv) } TestIoSys(tptr); - if(rank == 0 && tptr->params.stoneWallingWearOut){ + if (tptr->params.stoneWallingStatusFile){ + StoreStoneWallingIterations(tptr->params.stoneWallingStatusFile, tptr->results->pairs_accessed); + }else{ fprintf(out_logfile, "Pairs deadlineForStonewallingaccessed: %lld\n", (long long) tptr->results->pairs_accessed); } } @@ -796,6 +802,7 @@ static void DisplayUsage(char **argv) " -D N deadlineForStonewalling -- seconds before stopping write or read phase", " -O stoneWallingWearOut=1 -- once the stonewalling timout is over, all process finish to access the amount of data", " -O stoneWallingWearOutIterations=N -- stop after processing this number of iterations, needed for reading data back written with stoneWallingWearOut", + " -O stoneWallingStatusFile=FILE -- this file keeps the number of iterations from stonewalling during write and allows to use them for read" " -e fsync -- perform fsync/msync upon POSIX/MMAP write close", " -E useExistingTestFile -- do not remove test file before write access", " -f S scriptFile -- test script name", @@ -2707,6 +2714,13 @@ static IOR_offset_t WriteOrRead(IOR_param_t * test, IOR_results_t * results, voi && ((GetTimeStamp() - startForStonewall) > test->deadlineForStonewalling)); + if(access == READ && test->stoneWallingStatusFile){ + test->stoneWallingWearOutIterations = ReadStoneWallingIterations(test->stoneWallingStatusFile); + if(test->stoneWallingWearOutIterations == -1){ + ERR("Could not read back the stonewalling status from the file!"); + } + } + /* loop over offsets to access */ while ((offsetArray[pairCnt] != -1) && !hitStonewall ) { dataMoved += WriteOrReadSingle(pairCnt, offsetArray, pretendRank, & transferCount, & errors, test, fd, ioBuffers, access); diff --git a/src/ior.h b/src/ior.h index ec386d7..76ea323 100755 --- a/src/ior.h +++ b/src/ior.h @@ -135,6 +135,8 @@ typedef struct int deadlineForStonewalling; /* max time in seconds to run any test phase */ int stoneWallingWearOut; /* wear out the stonewalling, once the timout is over, each process has to write the same amount */ uint64_t stoneWallingWearOutIterations; /* the number of iterations for the stonewallingWearOut, needed for readBack */ + char stoneWallingStatusFile[MAXPATHLEN]; + int maxTimeDuration; /* max time in minutes to run each test */ int outlierThreshold; /* warn on outlier N seconds from mean */ int verbose; /* verbosity */ diff --git a/src/mdtest.c b/src/mdtest.c index 838a586..a7b5ebc 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -174,39 +174,7 @@ typedef struct{ /* for making/removing unique directory && stating/deleting subdirectory */ enum {MK_UNI_DIR, STAT_SUB_DIR, READ_SUB_DIR, RM_SUB_DIR, RM_UNI_DIR}; -#ifdef __linux__ -#define FAIL(msg) do { \ - fprintf(out_logfile, "%s: Process %d(%s): FAILED in %s, %s: %s\n", \ - print_timestamp(), rank, hostname, __func__, \ - msg, strerror(errno)); \ - fflush(out_logfile); \ - MPI_Abort(testComm, 1); \ - } while(0) -#else -#define FAIL(msg) do { \ - fprintf(out_logfile, "%s: Process %d(%s): FAILED at %d, %s: %s\n", \ - print_timestamp(), rank, hostname, __LINE__, \ - msg, strerror(errno)); \ - fflush(out_logfile); \ - MPI_Abort(testComm, 1); \ - } while(0) -#endif -static char *print_timestamp() { - static char datestring[80]; - time_t cur_timestamp; - - - if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( out_logfile, "V-1: Entering print_timestamp...\n" ); - } - - fflush(out_logfile); - cur_timestamp = time(NULL); - strftime(datestring, 80, "%m/%d/%Y %T", localtime(&cur_timestamp)); - - return datestring; -} #if MPI_VERSION >= 3 int count_tasks_per_node(void) { @@ -2199,7 +2167,7 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * nodeCount = size / count_tasks_per_node(); if (rank == 0) { - fprintf(out_logfile, "-- started at %s --\n\n", print_timestamp()); + fprintf(out_logfile, "-- started at %s --\n\n", PrintTimestamp()); fprintf(out_logfile, "mdtest-%s was launched with %d total task(s) on %d node(s)\n", RELEASE_VERS, size, nodeCount); fflush(out_logfile); @@ -2570,7 +2538,7 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * if(CHECK_STONE_WALL(& progress)){ fprintf(out_logfile, "\n-- hit stonewall\n"); } - fprintf(out_logfile, "\n-- finished at %s --\n", print_timestamp()); + fprintf(out_logfile, "\n-- finished at %s --\n", PrintTimestamp()); fflush(out_logfile); } diff --git a/src/parse_options.c b/src/parse_options.c index 506df33..0ebb691 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -186,6 +186,8 @@ void DecodeDirective(char *line, IOR_param_t *params) params->stoneWallingWearOut = atoi(value); } else if (strcasecmp(option, "stoneWallingWearOutIterations") == 0) { params->stoneWallingWearOutIterations = atoll(value); + } else if (strcasecmp(option, "stoneWallingStatusFile") == 0) { + strcpy(params->stoneWallingStatusFile, value); } else if (strcasecmp(option, "maxtimeduration") == 0) { params->maxTimeDuration = atoi(value); } else if (strcasecmp(option, "outlierthreshold") == 0) { diff --git a/src/utilities.c b/src/utilities.c index ed6d830..f71d790 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -539,3 +539,50 @@ void init_clock(){ /* check for skew between tasks' start times */ wall_clock_deviation = TimeDeviation(); } + +char * PrintTimestamp() { + static char datestring[80]; + time_t cur_timestamp; + + if (( rank == 0 ) && ( verbose >= 1 )) { + fprintf( out_logfile, "V-1: Entering PrintTimestamp...\n" ); + } + + fflush(out_logfile); + cur_timestamp = time(NULL); + strftime(datestring, 80, "%m/%d/%Y %T", localtime(&cur_timestamp)); + + return datestring; +} + +int64_t ReadStoneWallingIterations(char * const filename){ + long long data; + if(rank != 0){ + MPI_Bcast( & data, 1, MPI_LONG_LONG_INT, 0, mpi_comm_world); + return data; + }else{ + FILE * out = fopen(filename, "r"); + if (out == NULL){ + return -1; + } + int ret = fscanf(out, "%lld", & data); + if (ret != 1){ + return -1; + } + fclose(out); + MPI_Bcast( & data, 1, MPI_LONG_LONG_INT, 0, mpi_comm_world); + return data; + } +} + +void StoreStoneWallingIterations(char * const filename, int64_t count){ + if(rank != 0){ + return; + } + FILE * out = fopen(filename, "w"); + if (out == NULL){ + FAIL("Cannot write to the stonewalling file!"); + } + fprintf(out, "%lld", (long long) count); + fclose(out); +} diff --git a/src/utilities.h b/src/utilities.h index cc50e1a..3865757 100755 --- a/src/utilities.h +++ b/src/utilities.h @@ -27,6 +27,24 @@ extern MPI_Comm testComm; extern MPI_Comm mpi_comm_world; extern FILE * out_logfile; +#ifdef __linux__ +#define FAIL(msg) do { \ + fprintf(out_logfile, "%s: Process %d: FAILED in %s, %s: %s\n", \ + PrintTimestamp(), rank, __func__, \ + msg, strerror(errno)); \ + fflush(out_logfile); \ + MPI_Abort(testComm, 1); \ + } while(0) +#else +#define FAIL(msg) do { \ + fprintf(out_logfile, "%s: Process %d: FAILED at %d, %s: %s\n", \ + PrintTimestamp(), rank, __LINE__, \ + msg, strerror(errno)); \ + fflush(out_logfile); \ + MPI_Abort(testComm, 1); \ + } while(0) +#endif + void set_o_direct_flag(int *fd); char *CurrentTimeString(void); @@ -38,8 +56,13 @@ void SeedRandGen(MPI_Comm); void SetHints (MPI_Info *, char *); void ShowHints (MPI_Info *); +/* Returns -1, if cannot be read */ +int64_t ReadStoneWallingIterations(char * const filename); +void StoreStoneWallingIterations(char * const filename, int64_t count); + void init_clock(void); double GetTimeStamp(void); +char * PrintTimestamp(); // TODO remove this function extern double wall_clock_deviation; extern double wall_clock_delta; diff --git a/testing/complex-tests.sh b/testing/complex-tests.sh index 665727b..a759cf2 100755 --- a/testing/complex-tests.sh +++ b/testing/complex-tests.sh @@ -8,6 +8,12 @@ ROOT=${0%/*} source $ROOT/test-lib.sh +#stonewalling tests +IOR 2 -a DUMMY -w -O stoneWallingStatusFile=stonewall.log -O stoneWallingWearOut=1 -D 1 -t 1000 -b 1000 -s 15 +IOR 2 -a DUMMY -r -O stoneWallingStatusFile=stonewall.log -D 1 -t 1000 -b 1000 -s 30 # max 15 still! +IOR 2 -a DUMMY -r -O stoneWallingStatusFile=stonewall.log -t 1000 -b 1000 -s 30 + + #shared tests IOR 2 -a POSIX -w -z -Y -e -i1 -m -t 100k -b 100k IOR 2 -a POSIX -w -k -e -i1 -m -t 100k -b 100k From b4f356a7b2e09e195ecaf25e2d82921351454a1d Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 14:14:55 +0100 Subject: [PATCH 25/80] Bugfix: StoneWallingStatusFile. --- src/ior.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ior.c b/src/ior.c index 4889109..5c708cf 100755 --- a/src/ior.c +++ b/src/ior.c @@ -100,7 +100,7 @@ IOR_test_t * ior_run(int argc, char **argv, MPI_Comm world_com, FILE * world_out TestIoSys(tptr); if(rank == 0 && tptr->params.stoneWallingWearOut){ - if (tptr->params.stoneWallingStatusFile){ + if (tptr->params.stoneWallingStatusFile[0]){ StoreStoneWallingIterations(tptr->params.stoneWallingStatusFile, tptr->results->pairs_accessed); }else{ fprintf(out_logfile, "Pairs deadlineForStonewallingaccessed: %lld\n", (long long) tptr->results->pairs_accessed); @@ -201,7 +201,7 @@ int ior_main(int argc, char **argv) } TestIoSys(tptr); - if (tptr->params.stoneWallingStatusFile){ + if (tptr->params.stoneWallingStatusFile[0]){ StoreStoneWallingIterations(tptr->params.stoneWallingStatusFile, tptr->results->pairs_accessed); }else{ fprintf(out_logfile, "Pairs deadlineForStonewallingaccessed: %lld\n", (long long) tptr->results->pairs_accessed); @@ -2714,7 +2714,7 @@ static IOR_offset_t WriteOrRead(IOR_param_t * test, IOR_results_t * results, voi && ((GetTimeStamp() - startForStonewall) > test->deadlineForStonewalling)); - if(access == READ && test->stoneWallingStatusFile){ + if(access == READ && test->stoneWallingStatusFile[0]){ test->stoneWallingWearOutIterations = ReadStoneWallingIterations(test->stoneWallingStatusFile); if(test->stoneWallingWearOutIterations == -1){ ERR("Could not read back the stonewalling status from the file!"); From 7bc868d5cf18b02ea52ed33d67c9825ca800ad13 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 20:01:03 +0100 Subject: [PATCH 26/80] Dummy AIORI supporting MDtest ops. --- src/aiori-DUMMY.c | 78 +++++++++++++++++++++++++++++++++++------- src/mdtest.c | 4 +-- testing/basic-tests.sh | 2 +- 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/aiori-DUMMY.c b/src/aiori-DUMMY.c index 3b7daf3..4cd1e38 100755 --- a/src/aiori-DUMMY.c +++ b/src/aiori-DUMMY.c @@ -14,26 +14,43 @@ #include "aiori.h" #include "utilities.h" +static char * current = (char*) 1; + static void *DUMMY_Create(char *testFileName, IOR_param_t * param) { - return 0; + if(verbose > 4){ + fprintf(out_logfile, "DUMMY create: %s = %p\n", testFileName, current); + } + return current++; } static void *DUMMY_Open(char *testFileName, IOR_param_t * param) { - return 0; + if(verbose > 4){ + fprintf(out_logfile, "DUMMY open: %s = %p\n", testFileName, current); + } + return current++; } static void DUMMY_Fsync(void *fd, IOR_param_t * param) { + if(verbose > 4){ + fprintf(out_logfile, "DUMMY fsync %p\n", fd); + } } static void DUMMY_Close(void *fd, IOR_param_t * param) { + if(verbose > 4){ + fprintf(out_logfile, "DUMMY close %p\n", fd); + } } static void DUMMY_Delete(char *testFileName, IOR_param_t * param) { + if(verbose > 4){ + fprintf(out_logfile, "DUMMY delete: %s\n", testFileName); + } } static void DUMMY_SetVersion(IOR_param_t * test) @@ -42,25 +59,62 @@ static void DUMMY_SetVersion(IOR_param_t * test) } static IOR_offset_t DUMMY_GetFileSize(IOR_param_t * test, MPI_Comm testComm, char *testFileName) -{ return 0; +{ + if(verbose > 4){ + fprintf(out_logfile, "DUMMY getFileSize: %s\n", testFileName); + } + return 0; } static IOR_offset_t DUMMY_Xfer(int access, void *file, IOR_size_t * buffer, IOR_offset_t length, IOR_param_t * param){ + if(verbose > 4){ + fprintf(out_logfile, "DUMMY xfer: %p\n", file); + } if (rank == 0){ usleep(100000); } return length; } +static int DUMMY_statfs (const char * path, ior_aiori_statfs_t * stat, IOR_param_t * param){ + stat->f_bsize = 1; + stat->f_blocks = 1; + stat->f_bfree = 1; + stat->f_bavail = 1; + stat->f_files = 1; + stat->f_ffree = 1; + return 0; +} + +static int DUMMY_mkdir (const char *path, mode_t mode, IOR_param_t * param){ + return 0; +} + +static int DUMMY_rmdir (const char *path, IOR_param_t * param){ + return 0; +} + +static int DUMMY_access (const char *path, int mode, IOR_param_t * param){ + return 0; +} + +static int DUMMY_stat (const char *path, struct stat *buf, IOR_param_t * param){ + return 0; +} ior_aiori_t dummy_aiori = { - .name = "DUMMY", - .create = DUMMY_Create, - .open = DUMMY_Open, - .xfer = DUMMY_Xfer, - .close = DUMMY_Close, - .delete = DUMMY_Delete, - .set_version = DUMMY_SetVersion, - .fsync = DUMMY_Fsync, - .get_file_size = DUMMY_GetFileSize, + "DUMMY", + DUMMY_Create, + DUMMY_Open, + DUMMY_Xfer, + DUMMY_Close, + DUMMY_Delete, + DUMMY_SetVersion, + DUMMY_Fsync, + DUMMY_GetFileSize, + DUMMY_statfs, + DUMMY_mkdir, + DUMMY_rmdir, + DUMMY_access, + DUMMY_stat }; diff --git a/src/mdtest.c b/src/mdtest.c index a7b5ebc..e0627a5 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -1614,8 +1614,8 @@ void valid_tests() { FAIL("-c not compatible with -B"); } - if ( strcasecmp(backend_name, "POSIX") != 0) { - FAIL("-a only supported interface is POSIX right now!"); + if ( strcasecmp(backend_name, "POSIX") != 0 && strcasecmp(backend_name, "DUMMY") != 0) { + FAIL("-a only supported interface is POSIX (and DUMMY) right now!"); } /* check for shared file incompatibilities */ diff --git a/testing/basic-tests.sh b/testing/basic-tests.sh index 01d7477..885a884 100755 --- a/testing/basic-tests.sh +++ b/testing/basic-tests.sh @@ -11,7 +11,7 @@ ROOT=${0%/*} source $ROOT/test-lib.sh MDTEST 1 -a POSIX -#MDTEST 2 -a POSIX -W 2 +MDTEST 2 -a POSIX -W 2 IOR 1 -a POSIX -w -z -F -Y -e -i1 -m -t 100k -b 1000k IOR 1 -a POSIX -w -z -F -k -e -i2 -m -t 100k -b 100k From 6f8d2e8845f96cb1e48f845aaa1f53e080a998b6 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 22:19:42 +0100 Subject: [PATCH 27/80] Some simplification / unification between IOR and MDTest. MDTest needs more refactoring to be really maintainable. --- src/ior.c | 75 +------------------------- src/ior.h | 3 +- src/mdtest.c | 71 +++---------------------- src/mdtest.h | 15 +++--- src/utilities.c | 136 ++++++++++++++++++++++++------------------------ src/utilities.h | 2 +- 6 files changed, 88 insertions(+), 214 deletions(-) diff --git a/src/ior.c b/src/ior.c index 5c708cf..d7dd096 100755 --- a/src/ior.c +++ b/src/ior.c @@ -549,77 +549,6 @@ static int CountErrors(IOR_param_t * test, int access, int errors) return (allErrors); } -/* - * Count the number of tasks that share a host. - * - * This function employees the gethostname() call, rather than using - * MPI_Get_processor_name(). We are interested in knowing the number - * of tasks that share a file system client (I/O node, compute node, - * whatever that may be). However on machines like BlueGene/Q, - * MPI_Get_processor_name() uniquely identifies a cpu in a compute node, - * not the node where the I/O is function shipped to. gethostname() - * is assumed to identify the shared filesystem client in more situations. - * - * NOTE: This also assumes that the task count on all nodes is equal - * to the task count on the host running MPI task 0. - */ -int CountTasksPerNode(int numTasks, MPI_Comm comm) -{ - /* for debugging and testing */ - if (getenv("IOR_FAKE_TASK_PER_NODES")){ - int tasksPerNode = atoi(getenv("IOR_FAKE_TASK_PER_NODES")); - int rank; - MPI_Comm_rank(comm, & rank); - if(rank == 0){ - printf("Fake tasks per node: using %d\n", tasksPerNode); - } - return tasksPerNode; - } - - char localhost[MAX_STR]; - char hostname0[MAX_STR]; - static int firstPass = TRUE; - unsigned count; - unsigned flag; - int rc; - - rc = gethostname(localhost, MAX_STR); - if (rc == -1) { - /* This node won't match task 0's hostname...except in the - case where ALL gethostname() calls fail, in which - case ALL nodes will appear to be on the same node. - We'll handle that later. */ - localhost[0] = '\0'; - if (rank == 0) - perror("gethostname() failed"); - } - - if (verbose >= VERBOSE_2 && firstPass) { - char tmp[MAX_STR]; - sprintf(tmp, "task %d on %s", rank, localhost); - OutputToRoot(numTasks, comm, tmp); - firstPass = FALSE; - } - - /* send task 0's hostname to all tasks */ - if (rank == 0) - strcpy(hostname0, localhost); - MPI_CHECK(MPI_Bcast(hostname0, MAX_STR, MPI_CHAR, 0, comm), - "broadcast of task 0's hostname failed"); - if (strcmp(hostname0, localhost) == 0) - flag = 1; - else - flag = 0; - - /* count the tasks share the same host as task 0 */ - MPI_Allreduce(&flag, &count, 1, MPI_UNSIGNED, MPI_SUM, comm); - - if (hostname0[0] == '\0') - count = 1; - - return (int)count; -} - /* * Allocate a page-aligned (required by O_DIRECT) buffer. */ @@ -1349,7 +1278,7 @@ static IOR_test_t *SetupTests(int argc, char **argv) IOR_test_t *tests, *testsHead; /* count the tasks per node */ - tasksPerNode = CountTasksPerNode(numTasksWorld, mpi_comm_world); + tasksPerNode = CountTasksPerNode(mpi_comm_world); testsHead = tests = ParseCommandLine(argc, argv); /* @@ -2042,7 +1971,7 @@ static void TestIoSys(IOR_test_t *test) "Using reorderTasks '-C' (expecting block, not cyclic, task assignment)\n"); fflush(out_logfile); } - params->tasksPerNode = CountTasksPerNode(params->numTasks, testComm); + params->tasksPerNode = CountTasksPerNode(testComm); /* setup timers */ for (i = 0; i < 12; i++) { diff --git a/src/ior.h b/src/ior.h index 76ea323..7ee0396 100755 --- a/src/ior.h +++ b/src/ior.h @@ -135,7 +135,7 @@ typedef struct int deadlineForStonewalling; /* max time in seconds to run any test phase */ int stoneWallingWearOut; /* wear out the stonewalling, once the timout is over, each process has to write the same amount */ uint64_t stoneWallingWearOutIterations; /* the number of iterations for the stonewallingWearOut, needed for readBack */ - char stoneWallingStatusFile[MAXPATHLEN]; + char stoneWallingStatusFile[MAXPATHLEN]; int maxTimeDuration; /* max time in minutes to run each test */ int outlierThreshold; /* warn on outlier N seconds from mean */ @@ -240,7 +240,6 @@ IOR_test_t *CreateTest(IOR_param_t *init_params, int test_num); void AllocResults(IOR_test_t *test); void GetPlatformName(char *); void init_IOR_Param_t(IOR_param_t *p); -int CountTasksPerNode(int numTasks, MPI_Comm comm); /* * This function runs IOR given by command line, useful for testing diff --git a/src/mdtest.c b/src/mdtest.c index e0627a5..81b1df3 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -86,7 +86,6 @@ #define LLU "%lu" -//int rank; static int size; static uint64_t *rand_array; static char testdir[MAX_LEN]; @@ -176,55 +175,6 @@ enum {MK_UNI_DIR, STAT_SUB_DIR, READ_SUB_DIR, RM_SUB_DIR, RM_UNI_DIR}; -#if MPI_VERSION >= 3 -int count_tasks_per_node(void) { - /* modern MPI provides a simple way to get the local process count */ - MPI_Comm shared_comm; - int rc, count; - - MPI_Comm_split_type (testComm, MPI_COMM_TYPE_SHARED, 0, MPI_INFO_NULL, &shared_comm); - - MPI_Comm_size (shared_comm, &count); - - MPI_Comm_free (&shared_comm); - - return count; -} -#else -int count_tasks_per_node(void) { - char localhost[MAX_LEN], - hostname[MAX_LEN]; - int count = 1, - i; - MPI_Status status; - - if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( out_logfile, "V-1: Entering count_tasks_per_node...\n" ); - fflush( out_logfile ); - } - - if (gethostname(localhost, MAX_LEN) != 0) { - FAIL("gethostname()"); - } - if (rank == 0) { - /* MPI_receive all hostnames, and compare to local hostname */ - for (i = 0; i < size-1; i++) { - MPI_Recv(hostname, MAX_LEN, MPI_CHAR, MPI_ANY_SOURCE, - MPI_ANY_TAG, testComm, &status); - if (strcmp(hostname, localhost) == 0) { - count++; - } - } - } else { - /* MPI_send hostname to root node */ - MPI_Send(localhost, MAX_LEN, MPI_CHAR, 0, 0, testComm); - } - MPI_Bcast(&count, 1, MPI_INT, 0, testComm); - - return(count); -} -#endif - void delay_secs(int delay) { @@ -1023,7 +973,6 @@ void directory_test(const int iteration, const int ntasks, const char *path, ran fflush( out_logfile ); } - double start_timer = GetTimeStamp(); /* remove directories */ if (collective_creates) { if (rank == 0) { @@ -1112,7 +1061,7 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro t[0] = MPI_Wtime(); /* create phase */ - if (create_only && ! CHECK_STONE_WALL(progress)) { + if (create_only ) { if (unique_dir_per_task) { unique_dir_access(MK_UNI_DIR, temp_path); if (!time_unique_dir_overhead) { @@ -1138,6 +1087,7 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro /* create files */ create_remove_items(0, 0, 1, 0, temp_path, 0, progress); if(stone_wall_timer_seconds){ + /* TODO */ if (verbose >= 1 ) { fprintf( out_logfile, "V-1: rank %d stonewall hit with %lld items\n", rank, progress->items_done ); fflush( out_logfile ); @@ -1176,7 +1126,7 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro t[1] = MPI_Wtime(); /* stat phase */ - if (stat_only && ! CHECK_STONE_WALL(progress)) { + if (stat_only ) { if (unique_dir_per_task) { unique_dir_access(STAT_SUB_DIR, temp_path); if (!time_unique_dir_overhead) { @@ -1205,7 +1155,7 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro t[2] = MPI_Wtime(); /* read phase */ - if (read_only && ! CHECK_STONE_WALL(progress)) { + if (read_only ) { if (unique_dir_per_task) { unique_dir_access(READ_SUB_DIR, temp_path); if (!time_unique_dir_overhead) { @@ -1233,7 +1183,7 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro } t[3] = MPI_Wtime(); - if (remove_only && ! CHECK_STONE_WALL(progress)) { + if (remove_only) { if (unique_dir_per_task) { unique_dir_access(RM_SUB_DIR, temp_path); if (!time_unique_dir_overhead) { @@ -1261,7 +1211,7 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro MPI_Barrier(testComm); } t[4] = MPI_Wtime(); - if (remove_only && ! CHECK_STONE_WALL(progress)) { + if (remove_only) { if (unique_dir_per_task) { unique_dir_access(RM_UNI_DIR, temp_path); } else { @@ -2009,9 +1959,6 @@ static void mdtest_iteration(int i, int j, MPI_Group testgroup, mdtest_results_t } MPI_Barrier(testComm); - if(CHECK_STONE_WALL(progress)){ - return; - } if (remove_only) { startCreate = MPI_Wtime(); if (unique_dir_per_task) { @@ -2164,7 +2111,7 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * pid = getpid(); uid = getuid(); - nodeCount = size / count_tasks_per_node(); + nodeCount = size / CountTasksPerNode(testComm); if (rank == 0) { fprintf(out_logfile, "-- started at %s --\n\n", PrintTimestamp()); @@ -2384,9 +2331,7 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * * range 0 .. 1. Multiply that by n and you get a number in * the range 0 .. n. */ - - uint64_t k = - ( uint64_t ) ((( double )rand() / ( double )RAND_MAX ) * ( double )n ); + uint64_t k = ( uint64_t ) ((( double )rand() / ( double )RAND_MAX ) * ( double )n ); /* * Now move the nth element to the kth (randomly chosen) diff --git a/src/mdtest.h b/src/mdtest.h index 897329f..6267282 100644 --- a/src/mdtest.h +++ b/src/mdtest.h @@ -21,14 +21,15 @@ typedef enum { typedef struct { - double rate[MDTEST_LAST_NUM]; - double time[MDTEST_LAST_NUM]; - uint64_t items[MDTEST_LAST_NUM]; + double rate[MDTEST_LAST_NUM]; /* Calculated throughput */ + double time[MDTEST_LAST_NUM]; /* Time */ + uint64_t items[MDTEST_LAST_NUM]; /* Number of operations done */ - uint64_t stonewall_last_item[MDTEST_LAST_NUM]; - double stonewall_time[MDTEST_LAST_NUM]; - uint64_t stonewall_item_min[MDTEST_LAST_NUM]; - uint64_t stonewall_item_sum[MDTEST_LAST_NUM]; + /* Statistics when hitting the stonewall */ + double stonewall_time[MDTEST_LAST_NUM]; /* runtime until completion / hit of the stonewall */ + uint64_t stonewall_last_item[MDTEST_LAST_NUM]; /* Max number of items a process has accessed */ + uint64_t stonewall_item_min[MDTEST_LAST_NUM]; /* Min number of items a process has accessed */ + uint64_t stonewall_item_sum[MDTEST_LAST_NUM]; /* Total number of items accessed until stonewall */ } mdtest_results_t; mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * out_logfile); diff --git a/src/utilities.c b/src/utilities.c index f71d790..05d6f46 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -130,77 +130,77 @@ void DumpBuffer(void *buffer, return; } /* DumpBuffer() */ -/* - * Sends all strings to root nodes and displays. - */ -void OutputToRoot(int numTasks, MPI_Comm comm, char *stringToDisplay) -{ - int i; - int swapNeeded = TRUE; - int pairsToSwap; - char **stringArray; - char tmpString[MAX_STR]; - MPI_Status status; +#if MPI_VERSION >= 3 +int CountTasksPerNode(MPI_Comm comm) { + /* modern MPI provides a simple way to get the local process count */ + MPI_Comm shared_comm; + int rc, count; - /* malloc string array */ - stringArray = (char **)malloc(sizeof(char *) * numTasks); - if (stringArray == NULL) - ERR("out of memory"); - for (i = 0; i < numTasks; i++) { - stringArray[i] = (char *)malloc(sizeof(char) * MAX_STR); - if (stringArray[i] == NULL) - ERR("out of memory"); - } + MPI_Comm_split_type (comm, MPI_COMM_TYPE_SHARED, 0, MPI_INFO_NULL, &shared_comm); + MPI_Comm_size (shared_comm, &count); + MPI_Comm_free (&shared_comm); - strcpy(stringArray[rank], stringToDisplay); - - if (rank == 0) { - /* MPI_receive all strings */ - for (i = 1; i < numTasks; i++) { - MPI_CHECK(MPI_Recv(stringArray[i], MAX_STR, MPI_CHAR, - MPI_ANY_SOURCE, MPI_ANY_TAG, comm, - &status), "MPI_Recv() error"); - } - } else { - /* MPI_send string to root node */ - MPI_CHECK(MPI_Send - (stringArray[rank], MAX_STR, MPI_CHAR, 0, 0, comm), - "MPI_Send() error"); - } - MPI_CHECK(MPI_Barrier(comm), "barrier error"); - - /* sort strings using bubblesort */ - if (rank == 0) { - pairsToSwap = numTasks - 1; - while (swapNeeded) { - swapNeeded = FALSE; - for (i = 0; i < pairsToSwap; i++) { - if (strcmp(stringArray[i], stringArray[i + 1]) > - 0) { - strcpy(tmpString, stringArray[i]); - strcpy(stringArray[i], - stringArray[i + 1]); - strcpy(stringArray[i + 1], tmpString); - swapNeeded = TRUE; - } - } - pairsToSwap--; - } - } - - /* display strings */ - if (rank == 0) { - for (i = 0; i < numTasks; i++) { - fprintf(out_logfile, "%s\n", stringArray[i]); - } - } - - /* free strings */ - for (i = 0; i < numTasks; i++) { - free(stringArray[i]); - } - free(stringArray); + return count; } +#else +/* + * Count the number of tasks that share a host. + * + * This function employees the gethostname() call, rather than using + * MPI_Get_processor_name(). We are interested in knowing the number + * of tasks that share a file system client (I/O node, compute node, + * whatever that may be). However on machines like BlueGene/Q, + * MPI_Get_processor_name() uniquely identifies a cpu in a compute node, + * not the node where the I/O is function shipped to. gethostname() + * is assumed to identify the shared filesystem client in more situations. + * + * NOTE: This also assumes that the task count on all nodes is equal + * to the task count on the host running MPI task 0. + */ +int CountTasksPerNode(MPI_Comm comm) { + /* for debugging and testing */ + if (getenv("IOR_FAKE_TASK_PER_NODES")){ + int tasksPerNode = atoi(getenv("IOR_FAKE_TASK_PER_NODES")); + int rank; + MPI_Comm_rank(comm, & rank); + if(rank == 0){ + printf("Fake tasks per node: using %d\n", tasksPerNode); + } + return tasksPerNode; + } + char localhost[MAX_LEN], + hostname[MAX_LEN]; + int count = 1, + i; + MPI_Status status; + + if (( rank == 0 ) && ( verbose >= 1 )) { + fprintf( out_logfile, "V-1: Entering count_tasks_per_node...\n" ); + fflush( out_logfile ); + } + + if (gethostname(localhost, MAX_LEN) != 0) { + FAIL("gethostname()"); + } + if (rank == 0) { + /* MPI_receive all hostnames, and compare to local hostname */ + for (i = 0; i < size-1; i++) { + MPI_Recv(hostname, MAX_LEN, MPI_CHAR, MPI_ANY_SOURCE, + MPI_ANY_TAG, testComm, &status); + if (strcmp(hostname, localhost) == 0) { + count++; + } + } + } else { + /* MPI_send hostname to root node */ + MPI_Send(localhost, MAX_LEN, MPI_CHAR, 0, 0, testComm); + } + MPI_Bcast(&count, 1, MPI_INT, 0, testComm); + + return(count); +} +#endif + /* * Extract key/value pair from hint string. diff --git a/src/utilities.h b/src/utilities.h index 3865757..9759752 100755 --- a/src/utilities.h +++ b/src/utilities.h @@ -48,13 +48,13 @@ extern FILE * out_logfile; void set_o_direct_flag(int *fd); char *CurrentTimeString(void); -void OutputToRoot(int, MPI_Comm, char *); int Regex(char *, char *); void ShowFileSystemSize(char *); void DumpBuffer(void *, size_t); void SeedRandGen(MPI_Comm); void SetHints (MPI_Info *, char *); void ShowHints (MPI_Info *); +int CountTasksPerNode(MPI_Comm comm); /* Returns -1, if cannot be read */ int64_t ReadStoneWallingIterations(char * const filename); From 48c17b4d8162336beec60995b1cfc15a943a3307 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 22:26:57 +0100 Subject: [PATCH 28/80] Extracted (nearly identical) function DelaySeconds. --- src/ior.c | 12 ------------ src/mdtest.c | 23 ++--------------------- src/utilities.c | 11 +++++++++++ src/utilities.h | 1 + 4 files changed, 14 insertions(+), 33 deletions(-) diff --git a/src/ior.c b/src/ior.c index d7dd096..93bdc69 100755 --- a/src/ior.c +++ b/src/ior.c @@ -670,18 +670,6 @@ static void DestroyTests(IOR_test_t *tests_head) } } -/* - * Sleep for 'delay' seconds. - */ -static void DelaySecs(int delay) -{ - if (rank == 0 && delay > 0) { - if (verbose >= VERBOSE_1) - fprintf(out_logfile, "delaying %d seconds . . .\n", delay); - sleep(delay); - } -} - /* * Display freespace (df). */ diff --git a/src/mdtest.c b/src/mdtest.c index 81b1df3..1f55bf1 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -174,25 +174,6 @@ typedef struct{ enum {MK_UNI_DIR, STAT_SUB_DIR, READ_SUB_DIR, RM_SUB_DIR, RM_UNI_DIR}; - -void delay_secs(int delay) { - - - if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( out_logfile, "V-1: Entering delay_secs...\n" ); - fflush( out_logfile ); - } - - if (rank == 0 && delay > 0) { - if (verbose >= 1) { - fprintf(out_logfile, "delaying %d seconds . . .\n", delay); - fflush(out_logfile); - } - sleep(delay); - } - MPI_Barrier(testComm); -} - void offset_timers(double * t, int tcount) { double toffset; int i; @@ -1938,13 +1919,13 @@ static void mdtest_iteration(int i, int j, MPI_Group testgroup, mdtest_results_t if (dirs_only && !shared_file) { if (pre_delay) { - delay_secs(pre_delay); + DelaySecs(pre_delay); } directory_test(j, i, unique_mk_dir, progress); } if (files_only) { if (pre_delay) { - delay_secs(pre_delay); + DelaySecs(pre_delay); } file_test(j, i, unique_mk_dir, progress); } diff --git a/src/utilities.c b/src/utilities.c index 05d6f46..a9f4910 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -586,3 +586,14 @@ void StoreStoneWallingIterations(char * const filename, int64_t count){ fprintf(out, "%lld", (long long) count); fclose(out); } + +/* + * Sleep for 'delay' seconds. + */ +void DelaySecs(int delay){ + if (rank == 0 && delay > 0) { + if (verbose >= VERBOSE_1) + fprintf(out_logfile, "delaying %d seconds . . .\n", delay); + sleep(delay); + } +} diff --git a/src/utilities.h b/src/utilities.h index 9759752..e1163da 100755 --- a/src/utilities.h +++ b/src/utilities.h @@ -55,6 +55,7 @@ void SeedRandGen(MPI_Comm); void SetHints (MPI_Info *, char *); void ShowHints (MPI_Info *); int CountTasksPerNode(MPI_Comm comm); +void DelaySecs(int delay); /* Returns -1, if cannot be read */ int64_t ReadStoneWallingIterations(char * const filename); From ed5787811ca52555e96b5d1ed6807f19796f09c1 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 22:39:14 +0100 Subject: [PATCH 29/80] Extracted max pathlen variable. --- src/ior.c | 6 +-- src/iordef.h | 1 - src/mdtest.c | 97 +++++++++++++++++++++++-------------------------- src/utilities.h | 6 +++ 4 files changed, 54 insertions(+), 56 deletions(-) diff --git a/src/ior.c b/src/ior.c index 93bdc69..52d126b 100755 --- a/src/ior.c +++ b/src/ior.c @@ -251,7 +251,7 @@ void init_IOR_Param_t(IOR_param_t * p) strncpy(p->api, default_aiori, MAX_STR); strncpy(p->platform, "HOST(OSTYPE)", MAX_STR); - strncpy(p->testFileName, "testFile", MAXPATHLEN); + strncpy(p->testFileName, "testFile", MAX_PATHLEN); p->nodes = 1; p->tasksPerNode = 1; @@ -425,7 +425,7 @@ CompareBuffers(void *expectedBuffer, size_t size, IOR_offset_t transferCount, IOR_param_t *test, int access) { - char testFileName[MAXPATHLEN]; + char testFileName[MAX_PATHLEN]; char bufferLabel1[MAX_STR]; char bufferLabel2[MAX_STR]; size_t i, j, length, first, last; @@ -923,7 +923,7 @@ void GetPlatformName(char *platformName) static void GetTestFileName(char *testFileName, IOR_param_t * test) { char **fileNames; - char initialTestFileName[MAXPATHLEN]; + char initialTestFileName[MAX_PATHLEN]; char testFileNameRoot[MAX_STR]; char tmpString[MAX_STR]; int count; diff --git a/src/iordef.h b/src/iordef.h index e1784bd..f860c4f 100755 --- a/src/iordef.h +++ b/src/iordef.h @@ -32,7 +32,6 @@ # include # include -# define MAXPATHLEN 2048 # define F_OK 00 # define W_OK 02 # define R_OK 04 diff --git a/src/mdtest.c b/src/mdtest.c index 1f55bf1..5d0c682 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -73,13 +73,6 @@ #define FILEMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH #define DIRMODE S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IXOTH -/* - * Try using the system's PATH_MAX, which is what realpath and such use. - */ -#define MAX_LEN PATH_MAX -/* - #define MAX_LEN 1024 -*/ #define RELEASE_VERS "1.9.3" #define TEST_DIR "#test-dir" #define ITEM_COUNT 25000 @@ -88,23 +81,23 @@ static int size; static uint64_t *rand_array; -static char testdir[MAX_LEN]; -static char testdirpath[MAX_LEN]; -static char top_dir[MAX_LEN]; -static char base_tree_name[MAX_LEN]; +static char testdir[MAX_PATHLEN]; +static char testdirpath[MAX_PATHLEN]; +static char top_dir[MAX_PATHLEN]; +static char base_tree_name[MAX_PATHLEN]; static char **filenames; -static char hostname[MAX_LEN]; -static char unique_dir[MAX_LEN]; -static char mk_name[MAX_LEN]; -static char stat_name[MAX_LEN]; -static char read_name[MAX_LEN]; -static char rm_name[MAX_LEN]; -static char unique_mk_dir[MAX_LEN]; -static char unique_chdir_dir[MAX_LEN]; -static char unique_stat_dir[MAX_LEN]; -static char unique_read_dir[MAX_LEN]; -static char unique_rm_dir[MAX_LEN]; -static char unique_rm_uni_dir[MAX_LEN]; +static char hostname[MAX_PATHLEN]; +static char unique_dir[MAX_PATHLEN]; +static char mk_name[MAX_PATHLEN]; +static char stat_name[MAX_PATHLEN]; +static char read_name[MAX_PATHLEN]; +static char rm_name[MAX_PATHLEN]; +static char unique_mk_dir[MAX_PATHLEN]; +static char unique_chdir_dir[MAX_PATHLEN]; +static char unique_stat_dir[MAX_PATHLEN]; +static char unique_read_dir[MAX_PATHLEN]; +static char unique_rm_dir[MAX_PATHLEN]; +static char unique_rm_uni_dir[MAX_PATHLEN]; static char *write_buffer; static char *read_buffer; @@ -251,7 +244,7 @@ void unique_dir_access(int opt, char *to) { } static void create_remove_dirs (const char *path, bool create, uint64_t itemNum) { - char curr_item[MAX_LEN]; + char curr_item[MAX_PATHLEN]; const char *operation = create ? "create" : "remove"; if (( rank == 0 ) && @@ -281,7 +274,7 @@ static void create_remove_dirs (const char *path, bool create, uint64_t itemNum) } static void remove_file (const char *path, uint64_t itemNum) { - char curr_item[MAX_LEN]; + char curr_item[MAX_PATHLEN]; if (( rank == 0 ) && ( verbose >= 3 ) && @@ -304,7 +297,7 @@ static void remove_file (const char *path, uint64_t itemNum) { } static void create_file (const char *path, uint64_t itemNum) { - char curr_item[MAX_LEN]; + char curr_item[MAX_PATHLEN]; void *aiori_fh; if (( rank == 0 ) && @@ -382,7 +375,7 @@ static void create_file (const char *path, uint64_t itemNum) { void create_remove_items_helper(const int dirs, const int create, const char *path, uint64_t itemNum, rank_progress_t * progress) { - char curr_item[MAX_LEN]; + char curr_item[MAX_PATHLEN]; if (( rank == 0 ) && ( verbose >= 1 )) { fprintf( out_logfile, "V-1: Entering create_remove_items_helper...\n" ); @@ -409,7 +402,7 @@ void create_remove_items_helper(const int dirs, const int create, const char *pa /* helper function to do collective operations */ void collective_helper(const int dirs, const int create, const char* path, uint64_t itemNum, rank_progress_t * progress) { - char curr_item[MAX_LEN]; + char curr_item[MAX_PATHLEN]; if (( rank == 0 ) && ( verbose >= 1 )) { fprintf( out_logfile, "V-1: Entering collective_helper...\n" ); @@ -454,8 +447,8 @@ void collective_helper(const int dirs, const int create, const char* path, uint6 directory tree */ void create_remove_items(int currDepth, const int dirs, const int create, const int collective, const char *path, uint64_t dirNum, rank_progress_t * progress) { unsigned i; - char dir[MAX_LEN]; - char temp_path[MAX_LEN]; + char dir[MAX_PATHLEN]; + char temp_path[MAX_PATHLEN]; unsigned long long currDir = dirNum; @@ -465,7 +458,7 @@ void create_remove_items(int currDepth, const int dirs, const int create, const } - memset(dir, 0, MAX_LEN); + memset(dir, 0, MAX_PATHLEN); strcpy(temp_path, path); if (rank == 0 && verbose >= 3) { @@ -534,7 +527,7 @@ void create_remove_items(int currDepth, const int dirs, const int create, const void mdtest_stat(const int random, const int dirs, const char *path, rank_progress_t * progress) { struct stat buf; uint64_t parent_dir, item_num = 0; - char item[MAX_LEN], temp[MAX_LEN]; + char item[MAX_PATHLEN], temp[MAX_PATHLEN]; uint64_t stop; if (( rank == 0 ) && ( verbose >= 1 )) { @@ -556,10 +549,10 @@ void mdtest_stat(const int random, const int dirs, const char *path, rank_progre * be like passing char **. Tested it on a Cray and it seems to work either * way, but it seems that it is correct without the "&". * - memset(&item, 0, MAX_LEN); + memset(&item, 0, MAX_PATHLEN); */ - memset(item, 0, MAX_LEN); - memset(temp, 0, MAX_LEN); + memset(item, 0, MAX_PATHLEN); + memset(temp, 0, MAX_PATHLEN); /* determine the item number to stat */ if (random) { @@ -642,7 +635,7 @@ void mdtest_stat(const int random, const int dirs, const char *path, rank_progre /* reads all of the items created as specified by the input parameters */ void mdtest_read(int random, int dirs, char *path) { uint64_t stop, parent_dir, item_num = 0; - char item[MAX_LEN], temp[MAX_LEN]; + char item[MAX_PATHLEN], temp[MAX_PATHLEN]; void *aiori_fh; if (( rank == 0 ) && ( verbose >= 1 )) { @@ -674,10 +667,10 @@ void mdtest_read(int random, int dirs, char *path) { * * NTH: Both are technically correct in C. * - * memset(&item, 0, MAX_LEN); + * memset(&item, 0, MAX_PATHLEN); */ - memset(item, 0, MAX_LEN); - memset(temp, 0, MAX_LEN); + memset(item, 0, MAX_PATHLEN); + memset(temp, 0, MAX_PATHLEN); /* determine the item number to read */ if (random) { @@ -752,7 +745,7 @@ void mdtest_read(int random, int dirs, char *path) { /* This method should be called by rank 0. It subsequently does all of the creates and removes for the other ranks */ void collective_create_remove(const int create, const int dirs, const int ntasks, const char *path, rank_progress_t * progress) { - char temp[MAX_LEN]; + char temp[MAX_PATHLEN]; if (( rank == 0 ) && ( verbose >= 1 )) { fprintf( out_logfile, "V-1: Entering collective_create_remove...\n" ); @@ -761,7 +754,7 @@ void collective_create_remove(const int create, const int dirs, const int ntasks /* rank 0 does all of the creates and removes for all of the ranks */ for (int i = 0 ; i < ntasks ; ++i) { - memset(temp, 0, MAX_LEN); + memset(temp, 0, MAX_PATHLEN); strcpy(temp, testdir); strcat(temp, "/"); @@ -837,7 +830,7 @@ void collective_create_remove(const int create, const int dirs, const int ntasks void directory_test(const int iteration, const int ntasks, const char *path, rank_progress_t * progress) { int size; double t[5] = {0}; - char temp_path[MAX_LEN]; + char temp_path[MAX_PATHLEN]; MPI_Comm_size(testComm, &size); @@ -1030,7 +1023,7 @@ void directory_test(const int iteration, const int ntasks, const char *path, ran void file_test(const int iteration, const int ntasks, const char *path, rank_progress_t * progress) { int size; double t[5] = {0}; - char temp_path[MAX_LEN]; + char temp_path[MAX_PATHLEN]; MPI_Comm_size(testComm, &size); if (( rank == 0 ) && ( verbose >= 1 )) { @@ -1300,7 +1293,7 @@ void print_help (void) { } void summarize_results(int iterations) { - char access[MAX_LEN]; + char access[MAX_PATHLEN]; int i, j, k; int start, stop, tableSize = MDTEST_LAST_NUM; double min, max, mean, sd, sum = 0, var = 0, curr = 0; @@ -1585,9 +1578,9 @@ void valid_tests() { } void show_file_system_size(char *file_system) { - char real_path[MAX_LEN]; - char file_system_unit_str[MAX_LEN] = "GiB"; - char inode_unit_str[MAX_LEN] = "Mi"; + char real_path[MAX_PATHLEN]; + char file_system_unit_str[MAX_PATHLEN] = "GiB"; + char inode_unit_str[MAX_PATHLEN] = "Mi"; int64_t file_system_unit_val = 1024 * 1024 * 1024; int64_t inode_unit_val = 1024 * 1024; int64_t total_file_system_size, @@ -1649,7 +1642,7 @@ void show_file_system_size(char *file_system) { void display_freespace(char *testdirpath) { - char dirpath[MAX_LEN] = {0}; + char dirpath[MAX_PATHLEN] = {0}; int i; int directoryFound = 0; @@ -1700,7 +1693,7 @@ void create_remove_directory_tree(int create, int currDepth, char* path, int dirNum, rank_progress_t * progress) { unsigned i; - char dir[MAX_LEN]; + char dir[MAX_PATHLEN]; if (( rank == 0 ) && ( verbose >= 1 )) { @@ -1736,7 +1729,7 @@ void create_remove_directory_tree(int create, } } else if (currDepth <= depth) { - char temp_path[MAX_LEN]; + char temp_path[MAX_PATHLEN]; strcpy(temp_path, path); int currDir = dirNum; @@ -2336,7 +2329,7 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * /* setup directory path to work in */ if (path_count == 0) { /* special case where no directory path provided with '-d' option */ - char * dir = getcwd(testdirpath, MAX_LEN); + char * dir = getcwd(testdirpath, MAX_PATHLEN); path_count = 1; } else { strcpy(testdirpath, filenames[rank%path_count]); @@ -2373,7 +2366,7 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * } } - if (gethostname(hostname, MAX_LEN) == -1) { + if (gethostname(hostname, MAX_PATHLEN) == -1) { perror("gethostname"); MPI_Abort(testComm, 2); } diff --git a/src/utilities.h b/src/utilities.h index e1163da..0da2481 100755 --- a/src/utilities.h +++ b/src/utilities.h @@ -27,6 +27,12 @@ extern MPI_Comm testComm; extern MPI_Comm mpi_comm_world; extern FILE * out_logfile; +/* + * Try using the system's PATH_MAX, which is what realpath and such use. + */ +#define MAX_PATHLEN PATH_MAX + + #ifdef __linux__ #define FAIL(msg) do { \ fprintf(out_logfile, "%s: Process %d: FAILED in %s, %s: %s\n", \ From b27b50184a27e34f0948b3f2927f0930532982c5 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 22:42:05 +0100 Subject: [PATCH 30/80] Bugfix Refactoring MaxLen --- src/utilities.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utilities.c b/src/utilities.c index a9f4910..80f7f66 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -168,8 +168,8 @@ int CountTasksPerNode(MPI_Comm comm) { } return tasksPerNode; } - char localhost[MAX_LEN], - hostname[MAX_LEN]; + char localhost[MAX_PATHLEN], + hostname[MAX_PATHLEN]; int count = 1, i; MPI_Status status; @@ -179,13 +179,13 @@ int CountTasksPerNode(MPI_Comm comm) { fflush( out_logfile ); } - if (gethostname(localhost, MAX_LEN) != 0) { + if (gethostname(localhost, MAX_PATHLEN) != 0) { FAIL("gethostname()"); } if (rank == 0) { /* MPI_receive all hostnames, and compare to local hostname */ for (i = 0; i < size-1; i++) { - MPI_Recv(hostname, MAX_LEN, MPI_CHAR, MPI_ANY_SOURCE, + MPI_Recv(hostname, MAX_PATHLEN, MPI_CHAR, MPI_ANY_SOURCE, MPI_ANY_TAG, testComm, &status); if (strcmp(hostname, localhost) == 0) { count++; @@ -193,7 +193,7 @@ int CountTasksPerNode(MPI_Comm comm) { } } else { /* MPI_send hostname to root node */ - MPI_Send(localhost, MAX_LEN, MPI_CHAR, 0, 0, testComm); + MPI_Send(localhost, MAX_PATHLEN, MPI_CHAR, 0, 0, testComm); } MPI_Bcast(&count, 1, MPI_INT, 0, testComm); From 31e3cc2b605d0d8237d62443a5b994b70354f7a7 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 22:46:13 +0100 Subject: [PATCH 31/80] Bugfix extraction further. --- src/utilities.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utilities.c b/src/utilities.c index 80f7f66..cf9f9d8 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -158,6 +158,8 @@ int CountTasksPerNode(MPI_Comm comm) { * to the task count on the host running MPI task 0. */ int CountTasksPerNode(MPI_Comm comm) { + int size; + MPI_Comm_size(comm, & size); /* for debugging and testing */ if (getenv("IOR_FAKE_TASK_PER_NODES")){ int tasksPerNode = atoi(getenv("IOR_FAKE_TASK_PER_NODES")); @@ -183,7 +185,7 @@ int CountTasksPerNode(MPI_Comm comm) { FAIL("gethostname()"); } if (rank == 0) { - /* MPI_receive all hostnames, and compare to local hostname */ + /* MPI_receive all hostnames, and compares them to the local hostname */ for (i = 0; i < size-1; i++) { MPI_Recv(hostname, MAX_PATHLEN, MPI_CHAR, MPI_ANY_SOURCE, MPI_ANY_TAG, testComm, &status); From 58f13ba352b156fe4823db111d85a8821bb5c87f Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 22:54:40 +0100 Subject: [PATCH 32/80] Bugfix: extraction to count ranks per host used local communicator. --- src/utilities.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utilities.c b/src/utilities.c index cf9f9d8..962ccb2 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -188,16 +188,16 @@ int CountTasksPerNode(MPI_Comm comm) { /* MPI_receive all hostnames, and compares them to the local hostname */ for (i = 0; i < size-1; i++) { MPI_Recv(hostname, MAX_PATHLEN, MPI_CHAR, MPI_ANY_SOURCE, - MPI_ANY_TAG, testComm, &status); + MPI_ANY_TAG, comm, &status); if (strcmp(hostname, localhost) == 0) { count++; } } } else { /* MPI_send hostname to root node */ - MPI_Send(localhost, MAX_PATHLEN, MPI_CHAR, 0, 0, testComm); + MPI_Send(localhost, MAX_PATHLEN, MPI_CHAR, 0, 0, comm); } - MPI_Bcast(&count, 1, MPI_INT, 0, testComm); + MPI_Bcast(&count, 1, MPI_INT, 0, comm); return(count); } From fd4c306b98a568d4e236076d620096360490ddae Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 7 Jul 2018 23:56:39 +0100 Subject: [PATCH 33/80] MDTest stonewalling with status file. Appears to work; stonewall timer is honored only in the creation phase. This implies a limit to the objects that then are used for other phases. --- src/aiori-DUMMY.c | 3 + src/mdtest.c | 135 +++++++++++++++++++++------------------ testing/complex-tests.sh | 3 + 3 files changed, 79 insertions(+), 62 deletions(-) diff --git a/src/aiori-DUMMY.c b/src/aiori-DUMMY.c index 4cd1e38..eb54940 100755 --- a/src/aiori-DUMMY.c +++ b/src/aiori-DUMMY.c @@ -21,6 +21,9 @@ static void *DUMMY_Create(char *testFileName, IOR_param_t * param) if(verbose > 4){ fprintf(out_logfile, "DUMMY create: %s = %p\n", testFileName, current); } + if (rank == 0){ + usleep(100000); + } return current++; } diff --git a/src/mdtest.c b/src/mdtest.c index 5d0c682..c88e0f9 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -100,6 +100,8 @@ static char unique_rm_dir[MAX_PATHLEN]; static char unique_rm_uni_dir[MAX_PATHLEN]; static char *write_buffer; static char *read_buffer; +static char *stoneWallingStatusFile; + static int barriers; static int create_only; @@ -155,9 +157,10 @@ typedef struct{ double start_time; int stone_wall_timer_seconds; + + long long unsigned items_start; long long unsigned items_done; - int items_start; uint64_t items_per_dir; } rank_progress_t; @@ -222,8 +225,6 @@ void parse_dirpath(char *dirpath_arg) { */ void unique_dir_access(int opt, char *to) { - - if (( rank == 0 ) && ( verbose >= 1 )) { fprintf( out_logfile, "V-1: Entering unique_dir_access...\n" ); fflush( out_logfile ); @@ -382,7 +383,7 @@ void create_remove_items_helper(const int dirs, const int create, const char *pa fflush( out_logfile ); } - for (uint64_t i = progress->items_start ; i < progress->items_per_dir ; ++i) { + for (uint64_t i = progress->items_start; i < progress->items_per_dir ; ++i) { if (!dirs) { if (create) { create_file (path, itemNum + i); @@ -397,7 +398,7 @@ void create_remove_items_helper(const int dirs, const int create, const char *pa return; } } - progress->items_done = items_per_dir; + progress->items_done = progress->items_per_dir; } /* helper function to do collective operations */ @@ -408,7 +409,7 @@ void collective_helper(const int dirs, const int create, const char* path, uint6 fprintf( out_logfile, "V-1: Entering collective_helper...\n" ); fflush( out_logfile ); } - for (uint64_t i = 0 ; i < items_per_dir ; ++i) { + for (uint64_t i = progress->items_start ; i < progress->items_per_dir ; ++i) { if (dirs) { create_remove_dirs (path, create, itemNum + i); continue; @@ -440,7 +441,7 @@ void collective_helper(const int dirs, const int create, const char* path, uint6 return; } } - progress->items_done = items_per_dir; + progress->items_done = progress->items_per_dir; } /* recusive function to create and remove files/directories from the @@ -1020,6 +1021,36 @@ void directory_test(const int iteration, const int ntasks, const char *path, ran } } +/* Returns if the stonewall was hit */ +int updateStoneWallIterations(int iteration, rank_progress_t * progress, double tstart){ + int hit = 0; + if (verbose >= 1 ) { + fprintf( out_logfile, "V-1: rank %d stonewall hit with %lld items\n", rank, progress->items_done ); + fflush( out_logfile ); + } + progress->items_start = progress->items_done; + long long unsigned max_iter = 0; + MPI_Allreduce(& progress->items_done, & max_iter, 1, MPI_INT, MPI_MAX, testComm); + summary_table[iteration].stonewall_time[MDTEST_FILE_CREATE_NUM] = MPI_Wtime() - tstart; + + // continue to the maximum... + long long min_accessed = 0; + MPI_Reduce(& progress->items_done, & min_accessed, 1, MPI_LONG_LONG_INT, MPI_MIN, 0, testComm); + long long sum_accessed = 0; + MPI_Reduce(& progress->items_done, & sum_accessed, 1, MPI_LONG_LONG_INT, MPI_SUM, 0, testComm); + + if (rank == 0 && items != (sum_accessed / size)) { + summary_table[iteration].stonewall_item_sum[MDTEST_FILE_CREATE_NUM] = sum_accessed; + summary_table[iteration].stonewall_item_min[MDTEST_FILE_CREATE_NUM] = min_accessed * size; + fprintf( out_logfile, "V-1: continue stonewall hit min: %lld max: %lld avg: %.1f \n", min_accessed, max_iter, ((double) sum_accessed) / size); + fflush( out_logfile ); + hit = 1; + } + progress->items_per_dir = max_iter; + + return hit; +} + void file_test(const int iteration, const int ntasks, const char *path, rank_progress_t * progress) { int size; double t[5] = {0}; @@ -1061,37 +1092,27 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro /* create files */ create_remove_items(0, 0, 1, 0, temp_path, 0, progress); if(stone_wall_timer_seconds){ - /* TODO */ - if (verbose >= 1 ) { - fprintf( out_logfile, "V-1: rank %d stonewall hit with %lld items\n", rank, progress->items_done ); - fflush( out_logfile ); + int hit = updateStoneWallIterations(iteration, progress, t[0]); + + if (hit){ + progress->stone_wall_timer_seconds = 0; + create_remove_items(0, 0, 1, 0, temp_path, 0, progress); + // now reset the values + progress->stone_wall_timer_seconds = stone_wall_timer_seconds; + items = progress->items_per_dir; } - long long unsigned max_iter = 0; - MPI_Allreduce(& progress->items_done, & max_iter, 1, MPI_INT, MPI_MAX, testComm); - summary_table[iteration].stonewall_time[MDTEST_FILE_CREATE_NUM] = MPI_Wtime() - t[0]; - - // continue to the maximum... - long long min_accessed = 0; - MPI_Reduce(& progress->items_done, & min_accessed, 1, MPI_LONG_LONG_INT, MPI_MIN, 0, testComm); - - long long sum_accessed = 0; - MPI_Reduce(& progress->items_done, & sum_accessed, 1, MPI_LONG_LONG_INT, MPI_SUM, 0, testComm); - - if (rank == 0 && items != sum_accessed / size) { - summary_table[iteration].stonewall_item_sum[MDTEST_FILE_CREATE_NUM] = sum_accessed; - summary_table[iteration].stonewall_item_min[MDTEST_FILE_CREATE_NUM] = min_accessed * size; - fprintf( out_logfile, "V-1: continue stonewall hit min: %lld max: %lld avg: %.1f \n", min_accessed, max_iter, ((double) sum_accessed) / size); - fflush( out_logfile ); + if (stoneWallingStatusFile){ + StoreStoneWallingIterations(stoneWallingStatusFile, progress->items_per_dir); } - - progress->stone_wall_timer_seconds = 0; - progress->items_start = progress->items_done; - progress->items_per_dir = max_iter; - create_remove_items(0, 0, 1, 0, temp_path, 0, progress); - progress->stone_wall_timer_seconds = stone_wall_timer_seconds; - items = max_iter; - progress->items_done = max_iter; } + }else{ + if (stoneWallingStatusFile){ + /* The number of items depends on the stonewalling file */ + items = ReadStoneWallingIterations(stoneWallingStatusFile); + if (verbose >= 1 && rank == 0) { + printf("read stonewall file items: "LLU"\n", items); + } + } } if (barriers) { @@ -1116,11 +1137,7 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro } /* stat files */ - if (random_seed > 0) { - mdtest_stat(1,0,temp_path, progress); - } else { - mdtest_stat(0,0,temp_path, progress); - } + mdtest_stat((random_seed > 0 ? 1 : 0), 0, temp_path, progress); } if (barriers) { @@ -1279,7 +1296,8 @@ void print_help (void) { "\t-v: verbosity (each instance of option increments by one)\n" "\t-V: verbosity value\n" "\t-w: bytes to write to each file after it is created\n" - "\t-W: number in seconds; stonewall timer, write as many seconds and ensure all processes did the same number of operations\n" + "\t-W: number in seconds; stonewall timer, write as many seconds and ensure all processes did the same number of operations (currently only stops during create phase)\n" + "\t-x: StoneWallingStatusFile; contains the number of iterations of the creation phase, can be used to split phases across runs\n" "\t-y: sync file after writing\n" "\t-z: depth of hierarchical directory structure\n" "\t-Z: print time instead of rate\n" @@ -1769,7 +1787,14 @@ void create_remove_directory_tree(int create, } } -static void mdtest_iteration(int i, int j, MPI_Group testgroup, mdtest_results_t * summary_table, rank_progress_t * progress){ +static void mdtest_iteration(int i, int j, MPI_Group testgroup, mdtest_results_t * summary_table){ + rank_progress_t progress_o; + memset(& progress_o, 0 , sizeof(progress_o)); + progress_o.start_time = GetTimeStamp(); + progress_o.stone_wall_timer_seconds = stone_wall_timer_seconds; + progress_o.items_per_dir = items_per_dir; + rank_progress_t * progress = & progress_o; + /* start and end times of directory tree create/remove */ double startCreate, endCreate; int k, c; @@ -2024,6 +2049,7 @@ void mdtest_init_args(){ barriers = 1; branch_factor = 1; throttle = 1; + stoneWallingStatusFile = NULL; create_only = 0; stat_only = 0; read_only = 0; @@ -2107,7 +2133,7 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * verbose = 0; option_t *optList, *thisOpt; - optList = GetOptList(argc, argv, "a:b:BcCd:De:Ef:Fhi:I:l:Ln:N:p:rR::s:StTuvV:w:W:yz:Z"); + optList = GetOptList(argc, argv, "a:b:BcCd:De:Ef:Fhi:I:l:Ln:N:p:rR::s:StTuvV:w:W:x:yz:Z"); while (optList != NULL) { @@ -2186,6 +2212,8 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * write_bytes = ( size_t )strtoul( optarg, ( char ** )NULL, 10 ); break; case 'W': stone_wall_timer_seconds = atoi( optarg ); break; + case 'x': + stoneWallingStatusFile = strdup(optarg); break; case 'y': sync_file = 1; break; case 'z': @@ -2404,13 +2432,6 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * MPI_Comm_group(testComm, &worldgroup); - // keep track of the current status for stonewalling - rank_progress_t progress; - memset(& progress, 0 , sizeof(progress)); - progress.start_time = GetTimeStamp(); - progress.stone_wall_timer_seconds = stone_wall_timer_seconds; - progress.items_per_dir = items_per_dir; - /* Run the tests */ for (i = first; i <= last && i <= size; i += stride) { range.last = i - 1; @@ -2437,26 +2458,16 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * } for (j = 0; j < iterations; j++) { - mdtest_iteration(i, j, testgroup, & summary_table[j], & progress); - if(CHECK_STONE_WALL(& progress)){ - iterations = j + 1; - break; - } + // keep track of the current status for stonewalling + mdtest_iteration(i, j, testgroup, & summary_table[j]); } - items = progress.items_done; summarize_results(iterations); if (i == 1 && stride > 1) { i = 0; } - if(CHECK_STONE_WALL(& progress)){ - break; - } } if (rank == 0) { - if(CHECK_STONE_WALL(& progress)){ - fprintf(out_logfile, "\n-- hit stonewall\n"); - } fprintf(out_logfile, "\n-- finished at %s --\n", PrintTimestamp()); fflush(out_logfile); } diff --git a/testing/complex-tests.sh b/testing/complex-tests.sh index a759cf2..951dc32 100755 --- a/testing/complex-tests.sh +++ b/testing/complex-tests.sh @@ -13,6 +13,9 @@ IOR 2 -a DUMMY -w -O stoneWallingStatusFile=stonewall.log -O stoneWallingWearOut IOR 2 -a DUMMY -r -O stoneWallingStatusFile=stonewall.log -D 1 -t 1000 -b 1000 -s 30 # max 15 still! IOR 2 -a DUMMY -r -O stoneWallingStatusFile=stonewall.log -t 1000 -b 1000 -s 30 +MDTEST 2 -I 20 -a DUMMY -W 1 -x stonewall-md.log -C +MDTEST 2 -I 20 -a DUMMY -x stonewall-md.log -T -v +MDTEST 2 -I 20 -a DUMMY -x stonewall-md.log -D -v #shared tests IOR 2 -a POSIX -w -z -Y -e -i1 -m -t 100k -b 100k From d938f0efe1b1b0b99b87d8a9f4fc7558d3376b7d Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sun, 8 Jul 2018 00:16:30 +0100 Subject: [PATCH 34/80] MPIIO support fsync. --- src/aiori-MPIIO.c | 7 ++++--- src/ior.c | 17 ++++++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/aiori-MPIIO.c b/src/aiori-MPIIO.c index 9f5adce..b7ac933 100755 --- a/src/aiori-MPIIO.c +++ b/src/aiori-MPIIO.c @@ -370,9 +370,10 @@ static IOR_offset_t MPIIO_Xfer(int access, void *fd, IOR_size_t * buffer, /* * Perform fsync(). */ -static void MPIIO_Fsync(void *fd, IOR_param_t * param) +static void MPIIO_Fsync(void *fdp, IOR_param_t * param) { - ; + MPI_File * fd = (MPI_File*) fdp; + MPI_File_sync(*fd); } /* @@ -438,7 +439,7 @@ static IOR_offset_t SeekOffset(MPI_File fd, IOR_offset_t offset, if (param->filePerProc) { tempOffset = tempOffset / param->transferSize; } else { - /* + /* * this formula finds a file view offset for a task * from an absolute offset */ diff --git a/src/ior.c b/src/ior.c index 52d126b..c53487e 100755 --- a/src/ior.c +++ b/src/ior.c @@ -98,7 +98,6 @@ IOR_test_t * ior_run(int argc, char **argv, MPI_Comm world_com, FILE * world_out } TestIoSys(tptr); - if(rank == 0 && tptr->params.stoneWallingWearOut){ if (tptr->params.stoneWallingStatusFile[0]){ StoreStoneWallingIterations(tptr->params.stoneWallingStatusFile, tptr->results->pairs_accessed); @@ -201,10 +200,12 @@ int ior_main(int argc, char **argv) } TestIoSys(tptr); - if (tptr->params.stoneWallingStatusFile[0]){ - StoreStoneWallingIterations(tptr->params.stoneWallingStatusFile, tptr->results->pairs_accessed); - }else{ - fprintf(out_logfile, "Pairs deadlineForStonewallingaccessed: %lld\n", (long long) tptr->results->pairs_accessed); + if(rank == 0 && tptr->params.stoneWallingWearOut){ + if (tptr->params.stoneWallingStatusFile[0]){ + StoreStoneWallingIterations(tptr->params.stoneWallingStatusFile, tptr->results->pairs_accessed); + }else{ + fprintf(out_logfile, "Pairs deadlineForStonewallingaccessed: %lld\n", (long long) tptr->results->pairs_accessed); + } } } @@ -719,7 +720,7 @@ static void DisplayUsage(char **argv) " -D N deadlineForStonewalling -- seconds before stopping write or read phase", " -O stoneWallingWearOut=1 -- once the stonewalling timout is over, all process finish to access the amount of data", " -O stoneWallingWearOutIterations=N -- stop after processing this number of iterations, needed for reading data back written with stoneWallingWearOut", - " -O stoneWallingStatusFile=FILE -- this file keeps the number of iterations from stonewalling during write and allows to use them for read" + " -O stoneWallingStatusFile=FILE -- this file keeps the number of iterations from stonewalling during write and allows to use them for read", " -e fsync -- perform fsync/msync upon POSIX/MMAP write close", " -E useExistingTestFile -- do not remove test file before write access", " -f S scriptFile -- test script name", @@ -2313,7 +2314,9 @@ static void ValidateTests(IOR_param_t * test) if ((strcasecmp(test->api, "POSIX") != 0) && test->singleXferAttempt) WARN_RESET("retry only available in POSIX", test, &defaults, singleXferAttempt); - if ((strcasecmp(test->api, "POSIX") != 0) && (strcasecmp(test->api, "MMAP") != 0) + if ((strcasecmp(test->api, "POSIX") != 0) && + (strcasecmp(test->api, "MMAP") != 0) && + (strcasecmp(test->api, "MPIIO") != 0) && test->fsync) WARN_RESET("fsync() only available in POSIX/MMAP", test, &defaults, fsync); From 9e97a16fe45a9597b71b18f8e3a55bcc25ff8994 Mon Sep 17 00:00:00 2001 From: Enno Zickler Date: Wed, 22 Nov 2017 13:29:58 +0100 Subject: [PATCH 35/80] fix output to be allinge with column headers Signed-off-by: Julian M. Kunkel --- src/ior.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/ior.c b/src/ior.c index c53487e..0669741 100755 --- a/src/ior.c +++ b/src/ior.c @@ -1688,6 +1688,7 @@ static void PrintLongSummaryOneOperation(IOR_test_t *test, double *times, char * fprintf(out_logfile, "%10.2f ", bw->max / MEBIBYTE); fprintf(out_logfile, "%10.2f ", bw->min / MEBIBYTE); fprintf(out_logfile, "%10.2f ", bw->mean / MEBIBYTE); + fprintf(out_logfile, "%10.2f ", bw->sd / MEBIBYTE); fprintf(out_logfile, "%10.2f ", ops->max); fprintf(out_logfile, "%10.2f ", ops->min); @@ -1695,21 +1696,21 @@ static void PrintLongSummaryOneOperation(IOR_test_t *test, double *times, char * fprintf(out_logfile, "%10.2f ", ops->sd); fprintf(out_logfile, "%10.5f ", mean_of_array_of_doubles(times, reps)); - fprintf(out_logfile, "%d ", params->id); - fprintf(out_logfile, "%d ", params->numTasks); - fprintf(out_logfile, "%d ", params->tasksPerNode); - fprintf(out_logfile, "%d ", params->repetitions); - fprintf(out_logfile, "%d ", params->filePerProc); - fprintf(out_logfile, "%d ", params->reorderTasks); - fprintf(out_logfile, "%d ", params->taskPerNodeOffset); - fprintf(out_logfile, "%d ", params->reorderTasksRandom); - fprintf(out_logfile, "%d ", params->reorderTasksRandomSeed); - fprintf(out_logfile, "%lld ", params->segmentCount); - fprintf(out_logfile, "%lld ", params->blockSize); - fprintf(out_logfile, "%lld ", params->transferSize); - fprintf(out_logfile, "%lld ", results->aggFileSizeForBW[0]); - fprintf(out_logfile, "%s ", params->api); - fprintf(out_logfile, "%d", params->referenceNumber); + fprintf(out_logfile, "%5d ", params->id); + fprintf(out_logfile, "%6d ", params->numTasks); + fprintf(out_logfile, "%3d ", params->tasksPerNode); + fprintf(out_logfile, "%4d ", params->repetitions); + fprintf(out_logfile, "%3d ", params->filePerProc); + fprintf(out_logfile, "%5d ", params->reorderTasks); + fprintf(out_logfile, "%8d ", params->taskPerNodeOffset); + fprintf(out_logfile, "%9d ", params->reorderTasksRandom); + fprintf(out_logfile, "%4d ", params->reorderTasksRandomSeed); + fprintf(out_logfile, "%6lld ", params->segmentCount); + fprintf(out_logfile, "%8lld ", params->blockSize); + fprintf(out_logfile, "%8lld ", params->transferSize); + fprintf(out_logfile, "%9.1f ", (float)results->aggFileSizeForBW[0] / MEBIBYTE); + fprintf(out_logfile, "%3s ", params->api); + fprintf(out_logfile, "%6d", params->referenceNumber); fprintf(out_logfile, "\n"); fflush(out_logfile); @@ -1739,7 +1740,9 @@ static void PrintLongSummaryHeader() "Max(OPs)", "Min(OPs)", "Mean(OPs)", "StdDev", "Mean(s)"); fprintf(out_logfile, " Test# #Tasks tPN reps fPP reord reordoff reordrand seed" - " segcnt blksiz xsize aggsize API RefNum\n"); + " segcnt "); + fprintf(out_logfile, "%8s %8s %9s %5s", " blksiz", "xsize","aggs(MiB)", "API"); + fprintf(out_logfile, " RefNum\n"); } static void PrintLongSummaryAllTests(IOR_test_t *tests_head) From 7d3c7e901ec5f015cf928ed297d977e22bdfcc7f Mon Sep 17 00:00:00 2001 From: Shane Snyder Date: Wed, 25 Apr 2018 17:21:48 -0500 Subject: [PATCH 36/80] expose generic aiori_ calls for access, etc. Signed-off-by: Julian M. Kunkel --- src/aiori.c | 36 ++++++++++++++++++++++++++---------- src/aiori.h | 7 +++++++ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/aiori.c b/src/aiori.c index 69ed158..4e1f52a 100644 --- a/src/aiori.c +++ b/src/aiori.c @@ -65,7 +65,7 @@ ior_aiori_t *available_aiori[] = { * This function provides a AIORI statfs for POSIX-compliant filesystems. It * uses statvfs is available and falls back on statfs. */ -static int aiori_statfs (const char *path, ior_aiori_statfs_t *stat_buf, IOR_param_t * param) +int aiori_posix_statfs (const char *path, ior_aiori_statfs_t *stat_buf, IOR_param_t * param) { int ret; #if defined(HAVE_STATVFS) @@ -90,44 +90,60 @@ static int aiori_statfs (const char *path, ior_aiori_statfs_t *stat_buf, IOR_par return 0; } -static int aiori_mkdir (const char *path, mode_t mode, IOR_param_t * param) +int aiori_posix_mkdir (const char *path, mode_t mode, IOR_param_t * param) { return mkdir (path, mode); } -static int aiori_rmdir (const char *path, IOR_param_t * param) +int aiori_posix_rmdir (const char *path, IOR_param_t * param) { return rmdir (path); } -static int aiori_access (const char *path, int mode, IOR_param_t * param) +int aiori_posix_access (const char *path, int mode, IOR_param_t * param) { return access (path, mode); } -static int aiori_stat (const char *path, struct stat *buf, IOR_param_t * param) +int aiori_posix_stat (const char *path, struct stat *buf, IOR_param_t * param) { return stat (path, buf); } const ior_aiori_t *aiori_select (const char *api) { + char warn_str[256] = {0}; for (ior_aiori_t **tmp = available_aiori ; *tmp != NULL; ++tmp) { if (NULL == api || strcasecmp(api, (*tmp)->name) == 0) { if (NULL == (*tmp)->statfs) { - (*tmp)->statfs = aiori_statfs; + (*tmp)->statfs = aiori_posix_statfs; + snprintf(warn_str, 256, "assuming POSIX-based backend for" + " %s statfs call", api); + WARN(warn_str); } if (NULL == (*tmp)->mkdir) { - (*tmp)->mkdir = aiori_mkdir; + (*tmp)->mkdir = aiori_posix_mkdir; + snprintf(warn_str, 256, "assuming POSIX-based backend for" + " %s mkdir call", api); + WARN(warn_str); } if (NULL == (*tmp)->rmdir) { - (*tmp)->rmdir = aiori_rmdir; + (*tmp)->rmdir = aiori_posix_rmdir; + snprintf(warn_str, 256, "assuming POSIX-based backend for" + " %s rmdir call", api); + WARN(warn_str); } if (NULL == (*tmp)->access) { - (*tmp)->access = aiori_access; + (*tmp)->access = aiori_posix_access; + snprintf(warn_str, 256, "assuming POSIX-based backend for" + " %s access call", api); + WARN(warn_str); } if (NULL == (*tmp)->stat) { - (*tmp)->stat = aiori_stat; + (*tmp)->stat = aiori_posix_stat; + snprintf(warn_str, 256, "assuming POSIX-based backend for" + " %s stat call", api); + WARN(warn_str); } return *tmp; } diff --git a/src/aiori.h b/src/aiori.h index ee96795..a0f3ef5 100755 --- a/src/aiori.h +++ b/src/aiori.h @@ -94,6 +94,13 @@ const ior_aiori_t *aiori_select (const char *api); int aiori_count (void); const char *aiori_default (void); +/* some generic POSIX-based backend calls */ +int aiori_posix_statfs (const char *path, ior_aiori_statfs_t *stat_buf, IOR_param_t * param); +int aiori_posix_mkdir (const char *path, mode_t mode, IOR_param_t * param); +int aiori_posix_rmdir (const char *path, IOR_param_t * param); +int aiori_posix_access (const char *path, int mode, IOR_param_t * param); +int aiori_posix_stat (const char *path, struct stat *buf, IOR_param_t * param); + IOR_offset_t MPIIO_GetFileSize(IOR_param_t * test, MPI_Comm testComm, char *testFileName); From 1a3c92718b43feb099dddf6d19b9e9553a36596f Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sun, 8 Jul 2018 12:13:08 +0100 Subject: [PATCH 37/80] Fix merge --- src/aiori-MPIIO.c | 8 ++++---- src/aiori.h | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/aiori-MPIIO.c b/src/aiori-MPIIO.c index b7ac933..5979dfa 100755 --- a/src/aiori-MPIIO.c +++ b/src/aiori-MPIIO.c @@ -38,10 +38,10 @@ static void *MPIIO_Open(char *, IOR_param_t *); static IOR_offset_t MPIIO_Xfer(int, void *, IOR_size_t *, IOR_offset_t, IOR_param_t *); static void MPIIO_Close(void *, IOR_param_t *); -static void MPIIO_Delete(char *, IOR_param_t *); +void MPIIO_Delete(char *, IOR_param_t *); static void MPIIO_SetVersion(IOR_param_t *); static void MPIIO_Fsync(void *, IOR_param_t *); -static int MPIIO_Access(const char *, int, IOR_param_t *); +int MPIIO_Access(const char *, int, IOR_param_t *); /************************** D E C L A R A T I O N S ***************************/ @@ -63,7 +63,7 @@ ior_aiori_t mpiio_aiori = { /* * Try to access a file through the MPIIO interface. */ -static int MPIIO_Access(const char *path, int mode, IOR_param_t *param) +int MPIIO_Access(const char *path, int mode, IOR_param_t *param) { MPI_File fd; int mpi_mode = MPI_MODE_UNIQUE_OPEN; @@ -397,7 +397,7 @@ static void MPIIO_Close(void *fd, IOR_param_t * param) /* * Delete a file through the MPIIO interface. */ -static void MPIIO_Delete(char *testFileName, IOR_param_t * param) +void MPIIO_Delete(char *testFileName, IOR_param_t * param) { MPI_CHECK(MPI_File_delete(testFileName, (MPI_Info) MPI_INFO_NULL), "cannot delete file"); diff --git a/src/aiori.h b/src/aiori.h index a0f3ef5..a253aca 100755 --- a/src/aiori.h +++ b/src/aiori.h @@ -103,6 +103,9 @@ int aiori_posix_stat (const char *path, struct stat *buf, IOR_param_t * param); IOR_offset_t MPIIO_GetFileSize(IOR_param_t * test, MPI_Comm testComm, char *testFileName); +/* NOTE: these 3 MPI-IO functions are exported for reuse by HDF5/PNetCDF */ +void MPIIO_Delete(char *testFileName, IOR_param_t * param); +int MPIIO_Access(const char *, int, IOR_param_t *); void *POSIX_Create(char *testFileName, IOR_param_t *test); void *POSIX_Open(char *testFileName, IOR_param_t *test); From 8069e018e3f68a16fb86cfdbff253bb3814d470b Mon Sep 17 00:00:00 2001 From: Shane Snyder Date: Wed, 25 Apr 2018 17:34:25 -0500 Subject: [PATCH 38/80] make ncmpi/hdf5 use mpiio access/delete Signed-off-by: Julian M. Kunkel --- src/aiori-HDF5.c | 19 ++++++++++++++++--- src/aiori-MPIIO.c | 4 ++++ src/aiori-NCMPI.c | 19 ++++++++++++++++--- src/aiori-POSIX.c | 5 +++++ 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/aiori-HDF5.c b/src/aiori-HDF5.c index e957605..5d629e1 100755 --- a/src/aiori-HDF5.c +++ b/src/aiori-HDF5.c @@ -92,6 +92,7 @@ static void HDF5_Delete(char *, IOR_param_t *); static void HDF5_SetVersion(IOR_param_t *); static void HDF5_Fsync(void *, IOR_param_t *); static IOR_offset_t HDF5_GetFileSize(IOR_param_t *, MPI_Comm, char *); +static int HDF5_Access(const char *, int, IOR_param_t *); /************************** D E C L A R A T I O N S ***************************/ @@ -105,6 +106,11 @@ ior_aiori_t hdf5_aiori = { .set_version = HDF5_SetVersion, .fsync = HDF5_Fsync, .get_file_size = HDF5_GetFileSize, + .statfs = aiori_posix_statfs, + .mkdir = aiori_posix_mkdir, + .rmdir = aiori_posix_rmdir, + .access = HDF5_Access, + .stat = aiori_posix_stat, }; static hid_t xferPropList; /* xfer property list */ @@ -435,8 +441,7 @@ static void HDF5_Close(void *fd, IOR_param_t * param) */ static void HDF5_Delete(char *testFileName, IOR_param_t * param) { - if (unlink(testFileName) != 0) - WARN("cannot delete file"); + return(MPIIO_Delete(testFileName, param)); } /* @@ -565,5 +570,13 @@ static void SetupDataSet(void *fd, IOR_param_t * param) static IOR_offset_t HDF5_GetFileSize(IOR_param_t * test, MPI_Comm testComm, char *testFileName) { - return (MPIIO_GetFileSize(test, testComm, testFileName)); + return(MPIIO_GetFileSize(test, testComm, testFileName)); +} + +/* + * Use MPIIO call to check for access. + */ +static int HDF5_Access(const char *path, int mode, IOR_param_t *param) +{ + return(MPIIO_Access(path, mode, param)); } diff --git a/src/aiori-MPIIO.c b/src/aiori-MPIIO.c index 5979dfa..6251348 100755 --- a/src/aiori-MPIIO.c +++ b/src/aiori-MPIIO.c @@ -55,7 +55,11 @@ ior_aiori_t mpiio_aiori = { .set_version = MPIIO_SetVersion, .fsync = MPIIO_Fsync, .get_file_size = MPIIO_GetFileSize, + .statfs = aiori_posix_statfs, + .mkdir = aiori_posix_mkdir, + .rmdir = aiori_posix_rmdir, .access = MPIIO_Access, + .stat = aiori_posix_stat, }; /***************************** F U N C T I O N S ******************************/ diff --git a/src/aiori-NCMPI.c b/src/aiori-NCMPI.c index 98fd6bd..d990dfd 100755 --- a/src/aiori-NCMPI.c +++ b/src/aiori-NCMPI.c @@ -56,6 +56,7 @@ static void NCMPI_Delete(char *, IOR_param_t *); static void NCMPI_SetVersion(IOR_param_t *); static void NCMPI_Fsync(void *, IOR_param_t *); static IOR_offset_t NCMPI_GetFileSize(IOR_param_t *, MPI_Comm, char *); +static int NCMPI_Access(const char *, int, IOR_param_t *); /************************** D E C L A R A T I O N S ***************************/ @@ -69,6 +70,11 @@ ior_aiori_t ncmpi_aiori = { .set_version = NCMPI_SetVersion, .fsync = NCMPI_Fsync, .get_file_size = NCMPI_GetFileSize, + .statfs = aiori_posix_statfs, + .mkdir = aiori_posix_mkdir, + .rmdir = aiori_posix_rmdir, + .access = NCMPI_Access, + .stat = aiori_posix_stat, }; /***************************** F U N C T I O N S ******************************/ @@ -329,8 +335,7 @@ static void NCMPI_Close(void *fd, IOR_param_t * param) */ static void NCMPI_Delete(char *testFileName, IOR_param_t * param) { - if (unlink(testFileName) != 0) - WARN("unlink() failed"); + return(MPIIO_Delete(testFileName, param)); } /* @@ -387,5 +392,13 @@ static int GetFileMode(IOR_param_t * param) static IOR_offset_t NCMPI_GetFileSize(IOR_param_t * test, MPI_Comm testComm, char *testFileName) { - return (MPIIO_GetFileSize(test, testComm, testFileName)); + return(MPIIO_GetFileSize(test, testComm, testFileName)); +} + +/* + * Use MPIIO call to check for access. + */ +static int NCMPI_Access(const char *path, int mode, IOR_param_t *param) +{ + return(MPIIO_Access(path, mode, param)); } diff --git a/src/aiori-POSIX.c b/src/aiori-POSIX.c index ff5ddc2..7e0dbdf 100755 --- a/src/aiori-POSIX.c +++ b/src/aiori-POSIX.c @@ -81,6 +81,11 @@ ior_aiori_t posix_aiori = { .set_version = POSIX_SetVersion, .fsync = POSIX_Fsync, .get_file_size = POSIX_GetFileSize, + .statfs = aiori_posix_statfs, + .mkdir = aiori_posix_mkdir, + .rmdir = aiori_posix_rmdir, + .access = aiori_posix_access, + .stat = aiori_posix_stat, }; /***************************** F U N C T I O N S ******************************/ From 0052bff3e9d0881edde283d64ac8abb1e0a37be8 Mon Sep 17 00:00:00 2001 From: Shane Snyder Date: Wed, 25 Apr 2018 17:35:10 -0500 Subject: [PATCH 39/80] use backend->access in IOR source Signed-off-by: Julian M. Kunkel --- src/ior.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/ior.c b/src/ior.c index 0669741..bcc1266 100755 --- a/src/ior.c +++ b/src/ior.c @@ -1238,7 +1238,7 @@ static void RemoveFile(char *testFileName, int filePerProc, IOR_param_t * test) rankOffset = 0; GetTestFileName(testFileName, test); } - if (access(testFileName, F_OK) == 0) { + if (backend->access(testFileName, F_OK, test) == 0) { backend->delete(testFileName, test); } if (test->reorderTasksRandom == TRUE) { @@ -1246,13 +1246,7 @@ static void RemoveFile(char *testFileName, int filePerProc, IOR_param_t * test) GetTestFileName(testFileName, test); } } else { - // BUG: "access()" assumes a POSIX filesystem. Maybe use - // backend->get_file_size(), instead, (and catch - // errors), or extend the aiori struct to include - // something to safely check for existence of the - // "file". - // - if ((rank == 0) && (access(testFileName, F_OK) == 0)) { + if ((rank == 0) && (backend->access(testFileName, F_OK, test) == 0)) { backend->delete(testFileName, test); } } From 0f7a1f14b96306c79b448df5ee660be4ae017fb2 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sun, 8 Jul 2018 13:07:32 +0100 Subject: [PATCH 40/80] Support IO redirect from main() --- doc/USER_GUIDE | 31 ++++++++++++++++--------------- src/ior.c | 11 ++++++----- src/ior.h | 1 - src/iordef.h | 6 ++++++ src/parse_options.c | 15 +++++++++++++++ src/utilities.c | 1 + src/utilities.h | 1 + 7 files changed, 45 insertions(+), 21 deletions(-) diff --git a/doc/USER_GUIDE b/doc/USER_GUIDE index 9957fec..8581d3e 100755 --- a/doc/USER_GUIDE +++ b/doc/USER_GUIDE @@ -23,10 +23,10 @@ Index: ******************* * 1. DESCRIPTION * ******************* -IOR can be used for testing performance of parallel file systems using various -interfaces and access patterns. IOR uses MPI for process synchronization. -IOR version 2 is a complete rewrite of the original IOR (Interleaved-Or-Random) -version 1 code. +IOR can be used for testing performance of parallel file systems using various +interfaces and access patterns. IOR uses MPI for process synchronization. +IOR version 2 is a complete rewrite of the original IOR (Interleaved-Or-Random) +version 1 code. ****************** @@ -39,7 +39,7 @@ Two ways to run IOR: E.g., to execute: IOR -w -r -o filename This performs a write and a read to the file 'filename'. - * Command line with scripts -- any arguments on the command line will + * Command line with scripts -- any arguments on the command line will establish the default for the test run, but a script may be used in conjunction with this for varying specific tests during an execution of the code. @@ -125,7 +125,7 @@ GENERAL: S3_EMC, or NCMPI, depending on test [POSIX] * testFile - name of the output file [testFile] - NOTE: with filePerProc set, the tasks can round + NOTE: with filePerProc set, the tasks can round robin across multiple file names '-o S@S@S' * hintsFileName - name of the hints file [] @@ -267,7 +267,7 @@ GENERAL: data, this option measures the amount of data moved in a fixed amount of time. The objective is to prevent tasks slow to - complete from skewing the performance. + complete from skewing the performance. * setting this to zero (0) unsets this option * this option is incompatible w/data checking @@ -280,7 +280,8 @@ GENERAL: * summaryAlways - Always print the long summary for each test. Useful for long runs that may be interrupted, preventing the final long summary for ALL tests to be printed. - + * summaryFile=File - Output the summary to the file instead on stdout/stderr. + * summaryFormat=FMT - Choose the output format -- default, JSON, CSV POSIX-ONLY: =========== @@ -319,7 +320,7 @@ HDF5-ONLY: NOTE: default IOR creates a dataset the size of numTasks * blockSize to be accessed by all tasks - + * noFill - no pre-filling of data in HDF5 file creation [0=FALSE] * setAlignment - HDF5 alignment in bytes (e.g.: 8, 4k, 2m, 1g) [1] @@ -483,8 +484,8 @@ zip, gzip, and bzip. 2) gzip: For gzipped files, a transfer size of 1k is sufficient. -3) bzip2: For bziped files a transfer size of 1k is insufficient (~50% compressed). - To avoid compression a transfer size of greater than the bzip block size is required +3) bzip2: For bziped files a transfer size of 1k is insufficient (~50% compressed). + To avoid compression a transfer size of greater than the bzip block size is required (default = 900KB). I suggest a transfer size of greather than 1MB to avoid bzip2 compression. Be aware of the block size your compression algorithm will look at, and adjust the transfer size @@ -508,9 +509,9 @@ HOW DO I PERFORM MULTIPLE DATA CHECKS ON AN EXISTING FILE? and -r implied using both. This semantic has been subsequently altered to be omitting -w, -r, -W, and -R implied using both -w and -r.) - If you're running new tests to create a file and want repeat data checking on - this file multiple times, there is an undocumented option for this. It's -O - multiReRead=1, and you'd need to have an IOR version compiled with the + If you're running new tests to create a file and want repeat data checking on + this file multiple times, there is an undocumented option for this. It's -O + multiReRead=1, and you'd need to have an IOR version compiled with the USE_UNDOC_OPT=1 (in iordef.h). The command line would look like this: IOR -k -E -w -W -i 5 -o file -O multiReRead=1 @@ -586,7 +587,7 @@ HOW DO I USE STONEWALLING? actually reading the same amount from disk in the allotted time, but they are also reading the cached data from the previous test each time to get the increased performance. Setting -D high enough so that the cache is - overfilled will prevent this. + overfilled will prevent this. HOW DO I BYPASS CACHING WHEN READING BACK A FILE I'VE JUST WRITTEN? diff --git a/src/ior.c b/src/ior.c index bcc1266..7c574ff 100755 --- a/src/ior.c +++ b/src/ior.c @@ -90,6 +90,7 @@ IOR_test_t * ior_run(int argc, char **argv, MPI_Comm world_com, FILE * world_out for (tptr = tests_head; tptr != NULL; tptr = tptr->next) { totalErrorCount = 0; verbose = tptr->params.verbose; + tptr->params.testComm = world_com; if (rank == 0 && verbose >= VERBOSE_0) { ShowTestInfo(&tptr->params); } @@ -125,6 +126,7 @@ int ior_main(int argc, char **argv) int i; IOR_test_t *tests_head; IOR_test_t *tptr; + out_logfile = stdout; /* @@ -153,6 +155,7 @@ int ior_main(int argc, char **argv) MPI_CHECK(MPI_Comm_size(mpi_comm_world, &numTasksWorld), "cannot get number of tasks"); MPI_CHECK(MPI_Comm_rank(mpi_comm_world, &rank), "cannot get rank"); + PrintEarlyHeader(); /* set error-handling */ @@ -230,6 +233,8 @@ int ior_main(int argc, char **argv) aws_cleanup(); #endif + fflush(out_logfile); + return totalErrorCount; } @@ -1668,7 +1673,6 @@ static void PrintLongSummaryOneOperation(IOR_test_t *test, double *times, char * struct results *bw; struct results *ops; int reps; - if (rank != 0 || verbose < VERBOSE_0) return; @@ -1682,14 +1686,12 @@ static void PrintLongSummaryOneOperation(IOR_test_t *test, double *times, char * fprintf(out_logfile, "%10.2f ", bw->max / MEBIBYTE); fprintf(out_logfile, "%10.2f ", bw->min / MEBIBYTE); fprintf(out_logfile, "%10.2f ", bw->mean / MEBIBYTE); - fprintf(out_logfile, "%10.2f ", bw->sd / MEBIBYTE); fprintf(out_logfile, "%10.2f ", ops->max); fprintf(out_logfile, "%10.2f ", ops->min); fprintf(out_logfile, "%10.2f ", ops->mean); fprintf(out_logfile, "%10.2f ", ops->sd); - fprintf(out_logfile, "%10.5f ", - mean_of_array_of_doubles(times, reps)); + fprintf(out_logfile, "%10.5f ", mean_of_array_of_doubles(times, reps)); fprintf(out_logfile, "%5d ", params->id); fprintf(out_logfile, "%6d ", params->numTasks); fprintf(out_logfile, "%3d ", params->tasksPerNode); @@ -1742,7 +1744,6 @@ static void PrintLongSummaryHeader() static void PrintLongSummaryAllTests(IOR_test_t *tests_head) { IOR_test_t *tptr; - if (rank != 0 || verbose < VERBOSE_0) return; diff --git a/src/ior.h b/src/ior.h index 7ee0396..bfda441 100755 --- a/src/ior.h +++ b/src/ior.h @@ -82,7 +82,6 @@ typedef struct IO_BUFFERS typedef struct { - FILE * out_logfile; char debug[MAX_STR]; /* debug info string */ unsigned int mode; /* file permissions */ unsigned int openFlags; /* open flags (see also ) */ diff --git a/src/iordef.h b/src/iordef.h index f860c4f..23febd5 100755 --- a/src/iordef.h +++ b/src/iordef.h @@ -61,6 +61,12 @@ extern int verbose; /* verbose output */ /*************************** D E F I N I T I O N S ****************************/ +enum OutputFormat_t{ + OUTPUT_DEFAULT, + OUTPUT_CSV, + OUTPUT_JSON +}; + #ifndef FALSE # define FALSE 0 #endif /* not FALSE */ diff --git a/src/parse_options.c b/src/parse_options.c index 0ebb691..c58b735 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -170,6 +170,21 @@ void DecodeDirective(char *line, IOR_param_t *params) } if (strcasecmp(option, "api") == 0) { strcpy(params->api, value); + } else if (strcasecmp(option, "summaryFile") == 0) { + out_logfile = fopen(value, "w"); + if (out_logfile == NULL){ + FAIL("Cannot open output file for writes!"); + } + } else if (strcasecmp(option, "summaryFormat") == 0) { + if(strcasecmp(value, "default")){ + outputFormat = OUTPUT_DEFAULT; + }else if(strcasecmp(value, "JSON")){ + outputFormat = OUTPUT_JSON; + }else if(strcasecmp(value, "CSV")){ + outputFormat = OUTPUT_CSV; + }else{ + FAIL("Unknown summaryFormat"); + } } else if (strcasecmp(option, "refnum") == 0) { params->referenceNumber = atoi(value); } else if (strcasecmp(option, "debug") == 0) { diff --git a/src/utilities.c b/src/utilities.c index 962ccb2..a1dd25f 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -57,6 +57,7 @@ int verbose = VERBOSE_0; /* verbose output */ MPI_Comm testComm; MPI_Comm mpi_comm_world; FILE * out_logfile; +enum OutputFormat_t outputFormat; /***************************** F U N C T I O N S ******************************/ diff --git a/src/utilities.h b/src/utilities.h index 0da2481..21a7dfb 100755 --- a/src/utilities.h +++ b/src/utilities.h @@ -26,6 +26,7 @@ extern int verbose; extern MPI_Comm testComm; extern MPI_Comm mpi_comm_world; extern FILE * out_logfile; +extern enum OutputFormat_t outputFormat; /* format of the output */ /* * Try using the system's PATH_MAX, which is what realpath and such use. From 02883d4a63afe3036bcb62cbe5b574044c486d9e Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sun, 8 Jul 2018 13:38:05 +0100 Subject: [PATCH 41/80] Moved most output to ior-output.c --- src/Makefile.am | 4 +- src/aiori.c | 7 +- src/ior-internal.h | 35 +++ src/ior-output.c | 562 +++++++++++++++++++++++++++++++++++ src/ior.c | 699 +++----------------------------------------- src/parse_options.c | 1 + src/utilities.c | 56 ++++ src/utilities.h | 1 + 8 files changed, 708 insertions(+), 657 deletions(-) create mode 100644 src/ior-internal.h create mode 100644 src/ior-output.c diff --git a/src/Makefile.am b/src/Makefile.am index 4bc3068..4369c77 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,14 +3,14 @@ if USE_CAPS bin_PROGRAMS += IOR MDTEST endif -noinst_HEADERS = ior.h utilities.h parse_options.h aiori.h iordef.h getopt/optlist.h +noinst_HEADERS = ior.h utilities.h parse_options.h aiori.h iordef.h getopt/optlist.h ior-internal.h extraSOURCES = aiori.c aiori-DUMMY.c extraLDADD = extraLDFLAGS = extraCPPFLAGS = -ior_SOURCES = ior-main.c ior.c utilities.c parse_options.c getopt/optlist.c +ior_SOURCES = ior-main.c ior.c utilities.c parse_options.c getopt/optlist.c ior-output.c ior_LDFLAGS = ior_LDADD = ior_CPPFLAGS = diff --git a/src/aiori.c b/src/aiori.c index 4e1f52a..a32a147 100644 --- a/src/aiori.c +++ b/src/aiori.c @@ -28,8 +28,10 @@ */ ior_aiori_t *available_aiori[] = { +#ifdef USE_POSIX_AIORI + &posix_aiori, +#endif & dummy_aiori, - #ifdef USE_HDF5_AIORI &hdf5_aiori, #endif @@ -42,9 +44,6 @@ ior_aiori_t *available_aiori[] = { #ifdef USE_NCMPI_AIORI &ncmpi_aiori, #endif -#ifdef USE_POSIX_AIORI - &posix_aiori, -#endif #ifdef USE_MMAP_AIORI &mmap_aiori, #endif diff --git a/src/ior-internal.h b/src/ior-internal.h new file mode 100644 index 0000000..40b1706 --- /dev/null +++ b/src/ior-internal.h @@ -0,0 +1,35 @@ +/* + * This file contains header information for support code that is only used within IOR. + * For code shared across benchmarks, see utilities.h + */ +#ifndef _IOR_INTERNAL_H +#define _IOR_INTERNAL_H + +/* Part of ior-output.c */ +void PrintEarlyHeader(); +void PrintHeader(int argc, char **argv); +void ShowTestInfo(IOR_param_t *params); +void ShowSetup(IOR_param_t *params); +void ShowTest(IOR_param_t * test); +void PrintShortSummary(IOR_test_t * test); +void PrintLongSummaryAllTests(IOR_test_t *tests_head); +void PrintLongSummaryHeader(); +void PrintLongSummaryOneTest(IOR_test_t *test); +void DisplayFreespace(IOR_param_t * test); +void GetTestFileName(char *, IOR_param_t *); +void PrintRemoveTiming(double start, double finish, int rep); +void PrintReducedResult(IOR_test_t *test, int access, double bw, double *diff_subset, double totalTime, int rep); +/* End of ior-output */ + +struct results { + double min; + double max; + double mean; + double var; + double sd; + double sum; + double *val; +}; + + +#endif diff --git a/src/ior-output.c b/src/ior-output.c new file mode 100644 index 0000000..26f189c --- /dev/null +++ b/src/ior-output.c @@ -0,0 +1,562 @@ +#ifndef _WIN32 +# include /* uname() */ +#endif + +#include + +#include "ior.h" +#include "ior-internal.h" +#include "utilities.h" + +extern char **environ; + +static struct results *bw_values(int reps, IOR_offset_t *agg_file_size, double *vals); +static struct results *ops_values(int reps, IOR_offset_t *agg_file_size, IOR_offset_t transfer_size, double *vals); +static double mean_of_array_of_doubles(double *values, int len); +static void PPDouble(int leftjustify, double number, char *append); + +void PrintReducedResult(IOR_test_t *test, int access, double bw, double *diff_subset, double totalTime, int rep){ + fprintf(out_logfile, "%-10s", access == WRITE ? "write" : "read"); + PPDouble(1, bw / MEBIBYTE, " "); + PPDouble(1, (double)test->params.blockSize / KIBIBYTE, " "); + PPDouble(1, (double)test->params.transferSize / KIBIBYTE, " "); + PPDouble(1, diff_subset[0], " "); + PPDouble(1, diff_subset[1], " "); + PPDouble(1, diff_subset[2], " "); + PPDouble(1, totalTime, " "); + fprintf(out_logfile, "%-4d\n", rep); + fflush(out_logfile); +} + + +/* + * Message to print immediately after MPI_Init so we know that + * ior has started. + */ +void PrintEarlyHeader() +{ + if (rank != 0) + return; + + fprintf(out_logfile, "IOR-" META_VERSION ": MPI Coordinated Test of Parallel I/O\n"); + fflush(out_logfile); +} + +void PrintHeader(int argc, char **argv) +{ + struct utsname unamebuf; + int i; + + if (rank != 0) + return; + + fprintf(out_logfile, "Began: %s", CurrentTimeString()); + fprintf(out_logfile, "Command line used: %s", argv[0]); + for (i = 1; i < argc; i++) { + fprintf(out_logfile, " \"%s\"", argv[i]); + } + fprintf(out_logfile, "\n"); + if (uname(&unamebuf) != 0) { + EWARN("uname failed"); + fprintf(out_logfile, "Machine: Unknown"); + } else { + fprintf(out_logfile, "Machine: %s %s", unamebuf.sysname, + unamebuf.nodename); + if (verbose >= VERBOSE_2) { + fprintf(out_logfile, " %s %s %s", unamebuf.release, + unamebuf.version, unamebuf.machine); + } + } + fprintf(out_logfile, "\n"); +#ifdef _NO_MPI_TIMER + if (verbose >= VERBOSE_2) + fprintf(out_logfile, "Using unsynchronized POSIX timer\n"); +#else /* not _NO_MPI_TIMER */ + if (MPI_WTIME_IS_GLOBAL) { + if (verbose >= VERBOSE_2) + fprintf(out_logfile, "Using synchronized MPI timer\n"); + } else { + if (verbose >= VERBOSE_2) + fprintf(out_logfile, "Using unsynchronized MPI timer\n"); + } +#endif /* _NO_MPI_TIMER */ + if (verbose >= VERBOSE_1) { + fprintf(out_logfile, "Start time skew across all tasks: %.02f sec\n", + wall_clock_deviation); + } + if (verbose >= VERBOSE_3) { /* show env */ + fprintf(out_logfile, "STARTING ENVIRON LOOP\n"); + for (i = 0; environ[i] != NULL; i++) { + fprintf(out_logfile, "%s\n", environ[i]); + } + fprintf(out_logfile, "ENDING ENVIRON LOOP\n"); + } + fflush(out_logfile); +} + +/* + * Print header information for test output. + */ +void ShowTestInfo(IOR_param_t *params) +{ + fprintf(out_logfile, "\n"); + fprintf(out_logfile, "Test %d started: %s", params->id, CurrentTimeString()); + if (verbose >= VERBOSE_1) { + /* if pvfs2:, then skip */ + if (Regex(params->testFileName, "^[a-z][a-z].*:") == 0) { + DisplayFreespace(params); + } + } + fflush(out_logfile); +} + +/* + * Show simple test output with max results for iterations. + */ +void ShowSetup(IOR_param_t *params) +{ + + if (strcmp(params->debug, "") != 0) { + fprintf(out_logfile, "\n*** DEBUG MODE ***\n"); + fprintf(out_logfile, "*** %s ***\n\n", params->debug); + } + fprintf(out_logfile, "Summary:\n"); + fprintf(out_logfile, "\tapi = %s\n", params->apiVersion); + fprintf(out_logfile, "\ttest filename = %s\n", params->testFileName); + fprintf(out_logfile, "\taccess = "); + fprintf(out_logfile, params->filePerProc ? "file-per-process" : "single-shared-file"); + if (verbose >= VERBOSE_1 && strcmp(params->api, "POSIX") != 0) { + fprintf(out_logfile, params->collective == FALSE ? ", independent" : ", collective"); + } + fprintf(out_logfile, "\n"); + if (verbose >= VERBOSE_1) { + if (params->segmentCount > 1) { + fprintf(out_logfile, + "\tpattern = strided (%d segments)\n", + (int)params->segmentCount); + } else { + fprintf(out_logfile, + "\tpattern = segmented (1 segment)\n"); + } + } + fprintf(out_logfile, "\tordering in a file ="); + if (params->randomOffset == FALSE) { + fprintf(out_logfile, " sequential offsets\n"); + } else { + fprintf(out_logfile, " random offsets\n"); + } + fprintf(out_logfile, "\tordering inter file="); + if (params->reorderTasks == FALSE && params->reorderTasksRandom == FALSE) { + fprintf(out_logfile, " no tasks offsets\n"); + } + if (params->reorderTasks == TRUE) { + fprintf(out_logfile, " constant task offsets = %d\n", + params->taskPerNodeOffset); + } + if (params->reorderTasksRandom == TRUE) { + fprintf(out_logfile, " random task offsets >= %d, seed=%d\n", + params->taskPerNodeOffset, params->reorderTasksRandomSeed); + } + fprintf(out_logfile, "\tclients = %d (%d per node)\n", + params->numTasks, params->tasksPerNode); + if (params->memoryPerTask != 0) + fprintf(out_logfile, "\tmemoryPerTask = %s\n", + HumanReadable(params->memoryPerTask, BASE_TWO)); + if (params->memoryPerNode != 0) + fprintf(out_logfile, "\tmemoryPerNode = %s\n", + HumanReadable(params->memoryPerNode, BASE_TWO)); + fprintf(out_logfile, "\trepetitions = %d\n", params->repetitions); + fprintf(out_logfile, "\txfersize = %s\n", + HumanReadable(params->transferSize, BASE_TWO)); + fprintf(out_logfile, "\tblocksize = %s\n", + HumanReadable(params->blockSize, BASE_TWO)); + fprintf(out_logfile, "\taggregate filesize = %s\n", + HumanReadable(params->expectedAggFileSize, BASE_TWO)); +#ifdef HAVE_LUSTRE_LUSTRE_USER_H + if (params->lustre_set_striping) { + fprintf(out_logfile, "\tLustre stripe size = %s\n", + ((params->lustre_stripe_size == 0) ? "Use default" : + HumanReadable(params->lustre_stripe_size, BASE_TWO))); + if (params->lustre_stripe_count == 0) { + fprintf(out_logfile, "\t stripe count = %s\n", "Use default"); + } else { + fprintf(out_logfile, "\t stripe count = %d\n", + params->lustre_stripe_count); + } + } +#endif /* HAVE_LUSTRE_LUSTRE_USER_H */ + if (params->deadlineForStonewalling > 0) { + fprintf(out_logfile, "\tUsing stonewalling = %d second(s)%s\n", + params->deadlineForStonewalling, params->stoneWallingWearOut ? " with phase out" : ""); + } + fflush(out_logfile); +} + +/* + * Show test description. + */ +void ShowTest(IOR_param_t * test) +{ + const char* data_packets[] = {"g", "t","o","i"}; + + fprintf(out_logfile, "TEST:\t%s=%d\n", "id", test->id); + fprintf(out_logfile, "\t%s=%d\n", "refnum", test->referenceNumber); + fprintf(out_logfile, "\t%s=%s\n", "api", test->api); + fprintf(out_logfile, "\t%s=%s\n", "platform", test->platform); + fprintf(out_logfile, "\t%s=%s\n", "testFileName", test->testFileName); + fprintf(out_logfile, "\t%s=%s\n", "hintsFileName", test->hintsFileName); + fprintf(out_logfile, "\t%s=%d\n", "deadlineForStonewall", + test->deadlineForStonewalling); + fprintf(out_logfile, "\t%s=%d\n", "stoneWallingWearOut", test->stoneWallingWearOut); + fprintf(out_logfile, "\t%s=%d\n", "maxTimeDuration", test->maxTimeDuration); + fprintf(out_logfile, "\t%s=%d\n", "outlierThreshold", + test->outlierThreshold); + fprintf(out_logfile, "\t%s=%s\n", "options", test->options); + fprintf(out_logfile, "\t%s=%d\n", "nodes", test->nodes); + fprintf(out_logfile, "\t%s=%lu\n", "memoryPerTask", (unsigned long) test->memoryPerTask); + fprintf(out_logfile, "\t%s=%lu\n", "memoryPerNode", (unsigned long) test->memoryPerNode); + fprintf(out_logfile, "\t%s=%d\n", "tasksPerNode", tasksPerNode); + fprintf(out_logfile, "\t%s=%d\n", "repetitions", test->repetitions); + fprintf(out_logfile, "\t%s=%d\n", "multiFile", test->multiFile); + fprintf(out_logfile, "\t%s=%d\n", "interTestDelay", test->interTestDelay); + fprintf(out_logfile, "\t%s=%d\n", "fsync", test->fsync); + fprintf(out_logfile, "\t%s=%d\n", "fsYncperwrite", test->fsyncPerWrite); + fprintf(out_logfile, "\t%s=%d\n", "useExistingTestFile", + test->useExistingTestFile); + fprintf(out_logfile, "\t%s=%d\n", "showHints", test->showHints); + fprintf(out_logfile, "\t%s=%d\n", "uniqueDir", test->uniqueDir); + fprintf(out_logfile, "\t%s=%d\n", "showHelp", test->showHelp); + fprintf(out_logfile, "\t%s=%d\n", "individualDataSets", + test->individualDataSets); + fprintf(out_logfile, "\t%s=%d\n", "singleXferAttempt", + test->singleXferAttempt); + fprintf(out_logfile, "\t%s=%d\n", "readFile", test->readFile); + fprintf(out_logfile, "\t%s=%d\n", "writeFile", test->writeFile); + fprintf(out_logfile, "\t%s=%d\n", "filePerProc", test->filePerProc); + fprintf(out_logfile, "\t%s=%d\n", "reorderTasks", test->reorderTasks); + fprintf(out_logfile, "\t%s=%d\n", "reorderTasksRandom", + test->reorderTasksRandom); + fprintf(out_logfile, "\t%s=%d\n", "reorderTasksRandomSeed", + test->reorderTasksRandomSeed); + fprintf(out_logfile, "\t%s=%d\n", "randomOffset", test->randomOffset); + fprintf(out_logfile, "\t%s=%d\n", "checkWrite", test->checkWrite); + fprintf(out_logfile, "\t%s=%d\n", "checkRead", test->checkRead); + fprintf(out_logfile, "\t%s=%d\n", "preallocate", test->preallocate); + fprintf(out_logfile, "\t%s=%d\n", "useFileView", test->useFileView); + fprintf(out_logfile, "\t%s=%lld\n", "setAlignment", test->setAlignment); + fprintf(out_logfile, "\t%s=%d\n", "storeFileOffset", test->storeFileOffset); + fprintf(out_logfile, "\t%s=%d\n", "useSharedFilePointer", + test->useSharedFilePointer); + fprintf(out_logfile, "\t%s=%d\n", "useO_DIRECT", test->useO_DIRECT); + fprintf(out_logfile, "\t%s=%d\n", "useStridedDatatype", + test->useStridedDatatype); + fprintf(out_logfile, "\t%s=%d\n", "keepFile", test->keepFile); + fprintf(out_logfile, "\t%s=%d\n", "keepFileWithError", + test->keepFileWithError); + fprintf(out_logfile, "\t%s=%d\n", "quitOnError", test->quitOnError); + fprintf(out_logfile, "\t%s=%d\n", "verbose", verbose); + fprintf(out_logfile, "\t%s=%s\n", "data packet type", data_packets[test->dataPacketType]); + fprintf(out_logfile, "\t%s=%d\n", "setTimeStampSignature/incompressibleSeed", + test->setTimeStampSignature); /* Seed value was copied into setTimeStampSignature as well */ + fprintf(out_logfile, "\t%s=%d\n", "collective", test->collective); + fprintf(out_logfile, "\t%s=%lld", "segmentCount", test->segmentCount); +#ifdef HAVE_GPFS_FCNTL_H + fprintf(out_logfile, "\t%s=%d\n", "gpfsHintAccess", test->gpfs_hint_access); + fprintf(out_logfile, "\t%s=%d\n", "gpfsReleaseToken", test->gpfs_release_token); +#endif + if (strcasecmp(test->api, "HDF5") == 0) { + fprintf(out_logfile, " (datasets)"); + } + fprintf(out_logfile, "\n"); + fprintf(out_logfile, "\t%s=%lld\n", "transferSize", test->transferSize); + fprintf(out_logfile, "\t%s=%lld\n", "blockSize", test->blockSize); +} + + +/* + * Summarize results + * + * operation is typically "write" or "read" + */ +void PrintLongSummaryOneOperation(IOR_test_t *test, double *times, char *operation) +{ + IOR_param_t *params = &test->params; + IOR_results_t *results = test->results; + struct results *bw; + struct results *ops; + int reps; + if (rank != 0 || verbose < VERBOSE_0) + return; + + reps = params->repetitions; + + bw = bw_values(reps, results->aggFileSizeForBW, times); + ops = ops_values(reps, results->aggFileSizeForBW, + params->transferSize, times); + + fprintf(out_logfile, "%-9s ", operation); + fprintf(out_logfile, "%10.2f ", bw->max / MEBIBYTE); + fprintf(out_logfile, "%10.2f ", bw->min / MEBIBYTE); + fprintf(out_logfile, "%10.2f ", bw->mean / MEBIBYTE); + fprintf(out_logfile, "%10.2f ", bw->sd / MEBIBYTE); + fprintf(out_logfile, "%10.2f ", ops->max); + fprintf(out_logfile, "%10.2f ", ops->min); + fprintf(out_logfile, "%10.2f ", ops->mean); + fprintf(out_logfile, "%10.2f ", ops->sd); + fprintf(out_logfile, "%10.5f ", mean_of_array_of_doubles(times, reps)); + fprintf(out_logfile, "%5d ", params->id); + fprintf(out_logfile, "%6d ", params->numTasks); + fprintf(out_logfile, "%3d ", params->tasksPerNode); + fprintf(out_logfile, "%4d ", params->repetitions); + fprintf(out_logfile, "%3d ", params->filePerProc); + fprintf(out_logfile, "%5d ", params->reorderTasks); + fprintf(out_logfile, "%8d ", params->taskPerNodeOffset); + fprintf(out_logfile, "%9d ", params->reorderTasksRandom); + fprintf(out_logfile, "%4d ", params->reorderTasksRandomSeed); + fprintf(out_logfile, "%6lld ", params->segmentCount); + fprintf(out_logfile, "%8lld ", params->blockSize); + fprintf(out_logfile, "%8lld ", params->transferSize); + fprintf(out_logfile, "%9.1f ", (float)results->aggFileSizeForBW[0] / MEBIBYTE); + fprintf(out_logfile, "%3s ", params->api); + fprintf(out_logfile, "%6d", params->referenceNumber); + fprintf(out_logfile, "\n"); + fflush(out_logfile); + + free(bw); + free(ops); +} + +void PrintLongSummaryOneTest(IOR_test_t *test) +{ + IOR_param_t *params = &test->params; + IOR_results_t *results = test->results; + + if (params->writeFile) + PrintLongSummaryOneOperation(test, results->writeTime, "write"); + if (params->readFile) + PrintLongSummaryOneOperation(test, results->readTime, "read"); +} + +void PrintLongSummaryHeader() +{ + if (rank != 0 || verbose < VERBOSE_0) + return; + + fprintf(out_logfile, "\n"); + fprintf(out_logfile, "%-9s %10s %10s %10s %10s %10s %10s %10s %10s %10s", + "Operation", "Max(MiB)", "Min(MiB)", "Mean(MiB)", "StdDev", + "Max(OPs)", "Min(OPs)", "Mean(OPs)", "StdDev", + "Mean(s)"); + fprintf(out_logfile, " Test# #Tasks tPN reps fPP reord reordoff reordrand seed" + " segcnt "); + fprintf(out_logfile, "%8s %8s %9s %5s", " blksiz", "xsize","aggs(MiB)", "API"); + fprintf(out_logfile, " RefNum\n"); +} + +void PrintLongSummaryAllTests(IOR_test_t *tests_head) +{ + IOR_test_t *tptr; + if (rank != 0 || verbose < VERBOSE_0) + return; + + fprintf(out_logfile, "\n"); + fprintf(out_logfile, "Summary of all tests:"); + PrintLongSummaryHeader(); + + for (tptr = tests_head; tptr != NULL; tptr = tptr->next) { + PrintLongSummaryOneTest(tptr); + } +} + +void PrintShortSummary(IOR_test_t * test) +{ + IOR_param_t *params = &test->params; + IOR_results_t *results = test->results; + double max_write = 0.0; + double max_read = 0.0; + double bw; + int reps; + int i; + + if (rank != 0 || verbose < VERBOSE_0) + return; + + reps = params->repetitions; + + max_write = results->writeTime[0]; + max_read = results->readTime[0]; + for (i = 0; i < reps; i++) { + bw = (double)results->aggFileSizeForBW[i]/results->writeTime[i]; + max_write = MAX(bw, max_write); + bw = (double)results->aggFileSizeForBW[i]/results->readTime[i]; + max_read = MAX(bw, max_read); + } + + fprintf(out_logfile, "\n"); + if (params->writeFile) { + fprintf(out_logfile, "Max Write: %.2f MiB/sec (%.2f MB/sec)\n", + max_write/MEBIBYTE, max_write/MEGABYTE); + } + if (params->readFile) { + fprintf(out_logfile, "Max Read: %.2f MiB/sec (%.2f MB/sec)\n", + max_read/MEBIBYTE, max_read/MEGABYTE); + } +} + + +/* + * Display freespace (df). + */ +void DisplayFreespace(IOR_param_t * test) +{ + char fileName[MAX_STR] = { 0 }; + int i; + int directoryFound = FALSE; + + /* get outfile name */ + GetTestFileName(fileName, test); + + /* get directory for outfile */ + i = strlen(fileName); + while (i-- > 0) { + if (fileName[i] == '/') { + fileName[i] = '\0'; + directoryFound = TRUE; + break; + } + } + + /* if no directory/, use '.' */ + if (directoryFound == FALSE) { + strcpy(fileName, "."); + } + + ShowFileSystemSize(fileName); + + return; +} + + +void PrintRemoveTiming(double start, double finish, int rep) +{ + if (rank != 0 || verbose < VERBOSE_0) + return; + + fprintf(out_logfile, "remove - - - - - - "); + PPDouble(1, finish-start, " "); + fprintf(out_logfile, "%-4d\n", rep); +} + + +/* + * Pretty Print a Double. The First parameter is a flag determining if left + * justification should be used. The third parameter a null-terminated string + * that should be appended to the number field. + */ +static void PPDouble(int leftjustify, double number, char *append) +{ + char format[16]; + int width = 10; + int precision; + + if (number < 0) { + fprintf(out_logfile, " - %s", append); + return; + } + + if (number < 1) + precision = 6; + else if (number < 3600) + precision = 2; + else + precision = 0; + + sprintf(format, "%%%s%d.%df%%s", + leftjustify ? "-" : "", + width, precision); + + fprintf(out_logfile, format, number, append); +} + + + +static struct results *bw_values(int reps, IOR_offset_t *agg_file_size, double *vals) +{ + struct results *r; + int i; + + r = (struct results *)malloc(sizeof(struct results) + + (reps * sizeof(double))); + if (r == NULL) + ERR("malloc failed"); + r->val = (double *)&r[1]; + + for (i = 0; i < reps; i++) { + r->val[i] = (double)agg_file_size[i] / vals[i]; + if (i == 0) { + r->min = r->val[i]; + r->max = r->val[i]; + r->sum = 0.0; + } + r->min = MIN(r->min, r->val[i]); + r->max = MAX(r->max, r->val[i]); + r->sum += r->val[i]; + } + r->mean = r->sum / reps; + r->var = 0.0; + for (i = 0; i < reps; i++) { + r->var += pow((r->mean - r->val[i]), 2); + } + r->var = r->var / reps; + r->sd = sqrt(r->var); + + return r; +} + +static struct results *ops_values(int reps, IOR_offset_t *agg_file_size, + IOR_offset_t transfer_size, + double *vals) +{ + struct results *r; + int i; + + r = (struct results *)malloc(sizeof(struct results) + + (reps * sizeof(double))); + if (r == NULL) + ERR("malloc failed"); + r->val = (double *)&r[1]; + + for (i = 0; i < reps; i++) { + r->val[i] = (double)agg_file_size[i] / transfer_size / vals[i]; + if (i == 0) { + r->min = r->val[i]; + r->max = r->val[i]; + r->sum = 0.0; + } + r->min = MIN(r->min, r->val[i]); + r->max = MAX(r->max, r->val[i]); + r->sum += r->val[i]; + } + r->mean = r->sum / reps; + r->var = 0.0; + for (i = 0; i < reps; i++) { + r->var += pow((r->mean - r->val[i]), 2); + } + r->var = r->var / reps; + r->sd = sqrt(r->var); + + return r; +} + + +static double mean_of_array_of_doubles(double *values, int len) +{ + double tot = 0.0; + int i; + + for (i = 0; i < len; i++) { + tot += values[i]; + } + return tot / len; + +} diff --git a/src/ior.c b/src/ior.c index 7c574ff..5d2902e 100755 --- a/src/ior.c +++ b/src/ior.c @@ -31,6 +31,7 @@ #include #include "ior.h" +#include "ior-internal.h" #include "aiori.h" #include "utilities.h" #include "parse_options.h" @@ -43,16 +44,9 @@ static const ior_aiori_t *backend; static void DestroyTests(IOR_test_t *tests_head); static void DisplayUsage(char **); -static void GetTestFileName(char *, IOR_param_t *); static char *PrependDir(IOR_param_t *, char *); static char **ParseFileName(char *, int *); -static void PrintEarlyHeader(); -static void PrintHeader(int argc, char **argv); static IOR_test_t *SetupTests(int, char **); -static void ShowTestInfo(IOR_param_t *); -static void ShowSetup(IOR_param_t *params); -static void ShowTest(IOR_param_t *); -static void PrintLongSummaryAllTests(IOR_test_t *tests_head); static void TestIoSys(IOR_test_t *); static void ValidateTests(IOR_param_t *); static IOR_offset_t WriteOrRead(IOR_param_t * test, IOR_results_t * results, void *fd, int access, IOR_io_buffers* ioBuffers); @@ -676,38 +670,6 @@ static void DestroyTests(IOR_test_t *tests_head) } } -/* - * Display freespace (df). - */ -static void DisplayFreespace(IOR_param_t * test) -{ - char fileName[MAX_STR] = { 0 }; - int i; - int directoryFound = FALSE; - - /* get outfile name */ - GetTestFileName(fileName, test); - - /* get directory for outfile */ - i = strlen(fileName); - while (i-- > 0) { - if (fileName[i] == '/') { - fileName[i] = '\0'; - directoryFound = TRUE; - break; - } - } - - /* if no directory/, use '.' */ - if (directoryFound == FALSE) { - strcpy(fileName, "."); - } - - ShowFileSystemSize(fileName); - - return; -} - /* * Display usage of script file. */ @@ -922,11 +884,53 @@ void GetPlatformName(char *platformName) sprintf(platformName, "%s(%s)", nodeName, sysName); } + + +/* + * Parse file name. + */ +static char **ParseFileName(char *name, int *count) +{ + char **fileNames, *tmp, *token; + char delimiterString[3] = { FILENAME_DELIMITER, '\n', '\0' }; + int i = 0; + + *count = 0; + tmp = name; + + /* pass one */ + /* if something there, count the first item */ + if (*tmp != '\0') { + (*count)++; + } + /* count the rest of the filenames */ + while (*tmp != '\0') { + if (*tmp == FILENAME_DELIMITER) { + (*count)++; + } + tmp++; + } + + fileNames = (char **)malloc((*count) * sizeof(char **)); + if (fileNames == NULL) + ERR("out of memory"); + + /* pass two */ + token = strtok(name, delimiterString); + while (token != NULL) { + fileNames[i] = token; + token = strtok(NULL, delimiterString); + i++; + } + return (fileNames); +} + + /* * Return test file name to access. * for single shared file, fileNames[0] is returned in testFileName */ -static void GetTestFileName(char *testFileName, IOR_param_t * test) +void GetTestFileName(char *testFileName, IOR_param_t * test) { char **fileNames; char initialTestFileName[MAX_PATHLEN]; @@ -971,130 +975,6 @@ static void GetTestFileName(char *testFileName, IOR_param_t * test) free (fileNames); } -/* - * Convert IOR_offset_t value to human readable string. This routine uses a - * statically-allocated buffer internally and so is not re-entrant. - */ -static char *HumanReadable(IOR_offset_t value, int base) -{ - static char valueStr[MAX_STR]; - IOR_offset_t m = 0, g = 0, t = 0; - char m_str[8], g_str[8], t_str[8]; - - if (base == BASE_TWO) { - m = MEBIBYTE; - g = GIBIBYTE; - t = GIBIBYTE * 1024llu; - strcpy(m_str, "MiB"); - strcpy(g_str, "GiB"); - strcpy(t_str, "TiB"); - } else if (base == BASE_TEN) { - m = MEGABYTE; - g = GIGABYTE; - t = GIGABYTE * 1000llu; - strcpy(m_str, "MB"); - strcpy(g_str, "GB"); - strcpy(t_str, "TB"); - } - - if (value >= t) { - if (value % t) { - snprintf(valueStr, MAX_STR-1, "%.2f %s", - (double)((double)value / t), t_str); - } else { - snprintf(valueStr, MAX_STR-1, "%d %s", (int)(value / t), t_str); - } - }else if (value >= g) { - if (value % g) { - snprintf(valueStr, MAX_STR-1, "%.2f %s", - (double)((double)value / g), g_str); - } else { - snprintf(valueStr, MAX_STR-1, "%d %s", (int)(value / g), g_str); - } - } else if (value >= m) { - if (value % m) { - snprintf(valueStr, MAX_STR-1, "%.2f %s", - (double)((double)value / m), m_str); - } else { - snprintf(valueStr, MAX_STR-1, "%d %s", (int)(value / m), m_str); - } - } else if (value >= 0) { - snprintf(valueStr, MAX_STR-1, "%d bytes", (int)value); - } else { - snprintf(valueStr, MAX_STR-1, "-"); - } - return valueStr; -} - -/* - * Parse file name. - */ -static char **ParseFileName(char *name, int *count) -{ - char **fileNames, *tmp, *token; - char delimiterString[3] = { FILENAME_DELIMITER, '\n', '\0' }; - int i = 0; - - *count = 0; - tmp = name; - - /* pass one */ - /* if something there, count the first item */ - if (*tmp != '\0') { - (*count)++; - } - /* count the rest of the filenames */ - while (*tmp != '\0') { - if (*tmp == FILENAME_DELIMITER) { - (*count)++; - } - tmp++; - } - - fileNames = (char **)malloc((*count) * sizeof(char **)); - if (fileNames == NULL) - ERR("out of memory"); - - /* pass two */ - token = strtok(name, delimiterString); - while (token != NULL) { - fileNames[i] = token; - token = strtok(NULL, delimiterString); - i++; - } - return (fileNames); -} - -/* - * Pretty Print a Double. The First parameter is a flag determining if left - * justification should be used. The third parameter a null-terminated string - * that should be appended to the number field. - */ -static void PPDouble(int leftjustify, double number, char *append) -{ - char format[16]; - int width = 10; - int precision; - - if (number < 0) { - fprintf(out_logfile, " - %s", append); - return; - } - - if (number < 1) - precision = 6; - else if (number < 3600) - precision = 2; - else - precision = 0; - - sprintf(format, "%%%s%d.%df%%s", - leftjustify ? "-" : "", - width, precision); - - fprintf(out_logfile, format, number, append); -} - /* * From absolute directory, insert rank as subdirectory. Allows each task * to write to its own directory. E.g., /dir/file => /dir//file. @@ -1163,14 +1043,13 @@ static char *PrependDir(IOR_param_t * test, char *rootDir) static void ReduceIterResults(IOR_test_t *test, double **timer, int rep, int access) { - double reduced[12] = { 0 }; + double reduced[12] = { 0 }; double diff[6]; double *diff_subset; double totalTime; double bw; - enum { RIGHT, LEFT }; - int i; - MPI_Op op; + int i; + MPI_Op op; assert(access == WRITE || access == READ); @@ -1205,28 +1084,9 @@ static void ReduceIterResults(IOR_test_t *test, double **timer, int rep, return; } - fprintf(out_logfile, "%-10s", access == WRITE ? "write" : "read"); bw = (double)test->results->aggFileSizeForBW[rep] / totalTime; - PPDouble(LEFT, bw / MEBIBYTE, " "); - PPDouble(LEFT, (double)test->params.blockSize / KIBIBYTE, " "); - PPDouble(LEFT, (double)test->params.transferSize / KIBIBYTE, " "); - PPDouble(LEFT, diff_subset[0], " "); - PPDouble(LEFT, diff_subset[1], " "); - PPDouble(LEFT, diff_subset[2], " "); - PPDouble(LEFT, totalTime, " "); - fprintf(out_logfile, "%-4d\n", rep); - fflush(out_logfile); -} - -static void PrintRemoveTiming(double start, double finish, int rep) -{ - if (rank != 0 || verbose < VERBOSE_0) - return; - - fprintf(out_logfile, "remove - - - - - - "); - PPDouble(1, finish-start, " "); - fprintf(out_logfile, "%-4d\n", rep); + PrintReducedResult(test, access, bw, diff_subset, totalTime, rep); } /* @@ -1327,469 +1187,6 @@ static void XferBuffersFree(IOR_io_buffers* ioBuffers, IOR_param_t* test) } -/* - * Message to print immediately after MPI_Init so we know that - * ior has started. - */ -static void PrintEarlyHeader() -{ - if (rank != 0) - return; - - fprintf(out_logfile, "IOR-" META_VERSION ": MPI Coordinated Test of Parallel I/O\n"); - fprintf(out_logfile, "\n"); - fflush(out_logfile); -} - -static void PrintHeader(int argc, char **argv) -{ - struct utsname unamebuf; - int i; - - if (rank != 0) - return; - - fprintf(out_logfile, "Began: %s", CurrentTimeString()); - fprintf(out_logfile, "Command line used: %s", argv[0]); - for (i = 1; i < argc; i++) { - fprintf(out_logfile, " \"%s\"", argv[i]); - } - fprintf(out_logfile, "\n"); - if (uname(&unamebuf) != 0) { - EWARN("uname failed"); - fprintf(out_logfile, "Machine: Unknown"); - } else { - fprintf(out_logfile, "Machine: %s %s", unamebuf.sysname, - unamebuf.nodename); - if (verbose >= VERBOSE_2) { - fprintf(out_logfile, " %s %s %s", unamebuf.release, - unamebuf.version, unamebuf.machine); - } - } - fprintf(out_logfile, "\n"); -#ifdef _NO_MPI_TIMER - if (verbose >= VERBOSE_2) - fprintf(out_logfile, "Using unsynchronized POSIX timer\n"); -#else /* not _NO_MPI_TIMER */ - if (MPI_WTIME_IS_GLOBAL) { - if (verbose >= VERBOSE_2) - fprintf(out_logfile, "Using synchronized MPI timer\n"); - } else { - if (verbose >= VERBOSE_2) - fprintf(out_logfile, "Using unsynchronized MPI timer\n"); - } -#endif /* _NO_MPI_TIMER */ - if (verbose >= VERBOSE_1) { - fprintf(out_logfile, "Start time skew across all tasks: %.02f sec\n", - wall_clock_deviation); - } - if (verbose >= VERBOSE_3) { /* show env */ - fprintf(out_logfile, "STARTING ENVIRON LOOP\n"); - for (i = 0; environ[i] != NULL; i++) { - fprintf(out_logfile, "%s\n", environ[i]); - } - fprintf(out_logfile, "ENDING ENVIRON LOOP\n"); - } - fflush(out_logfile); -} - -/* - * Print header information for test output. - */ -static void ShowTestInfo(IOR_param_t *params) -{ - fprintf(out_logfile, "\n"); - fprintf(out_logfile, "Test %d started: %s", params->id, CurrentTimeString()); - if (verbose >= VERBOSE_1) { - /* if pvfs2:, then skip */ - if (Regex(params->testFileName, "^[a-z][a-z].*:") == 0) { - DisplayFreespace(params); - } - } - fflush(out_logfile); -} - -/* - * Show simple test output with max results for iterations. - */ -static void ShowSetup(IOR_param_t *params) -{ - - if (strcmp(params->debug, "") != 0) { - fprintf(out_logfile, "\n*** DEBUG MODE ***\n"); - fprintf(out_logfile, "*** %s ***\n\n", params->debug); - } - fprintf(out_logfile, "Summary:\n"); - fprintf(out_logfile, "\tapi = %s\n", params->apiVersion); - fprintf(out_logfile, "\ttest filename = %s\n", params->testFileName); - fprintf(out_logfile, "\taccess = "); - fprintf(out_logfile, params->filePerProc ? "file-per-process" : "single-shared-file"); - if (verbose >= VERBOSE_1 && strcmp(params->api, "POSIX") != 0) { - fprintf(out_logfile, params->collective == FALSE ? ", independent" : ", collective"); - } - fprintf(out_logfile, "\n"); - if (verbose >= VERBOSE_1) { - if (params->segmentCount > 1) { - fprintf(out_logfile, - "\tpattern = strided (%d segments)\n", - (int)params->segmentCount); - } else { - fprintf(out_logfile, - "\tpattern = segmented (1 segment)\n"); - } - } - fprintf(out_logfile, "\tordering in a file ="); - if (params->randomOffset == FALSE) { - fprintf(out_logfile, " sequential offsets\n"); - } else { - fprintf(out_logfile, " random offsets\n"); - } - fprintf(out_logfile, "\tordering inter file="); - if (params->reorderTasks == FALSE && params->reorderTasksRandom == FALSE) { - fprintf(out_logfile, " no tasks offsets\n"); - } - if (params->reorderTasks == TRUE) { - fprintf(out_logfile, " constant task offsets = %d\n", - params->taskPerNodeOffset); - } - if (params->reorderTasksRandom == TRUE) { - fprintf(out_logfile, " random task offsets >= %d, seed=%d\n", - params->taskPerNodeOffset, params->reorderTasksRandomSeed); - } - fprintf(out_logfile, "\tclients = %d (%d per node)\n", - params->numTasks, params->tasksPerNode); - if (params->memoryPerTask != 0) - fprintf(out_logfile, "\tmemoryPerTask = %s\n", - HumanReadable(params->memoryPerTask, BASE_TWO)); - if (params->memoryPerNode != 0) - fprintf(out_logfile, "\tmemoryPerNode = %s\n", - HumanReadable(params->memoryPerNode, BASE_TWO)); - fprintf(out_logfile, "\trepetitions = %d\n", params->repetitions); - fprintf(out_logfile, "\txfersize = %s\n", - HumanReadable(params->transferSize, BASE_TWO)); - fprintf(out_logfile, "\tblocksize = %s\n", - HumanReadable(params->blockSize, BASE_TWO)); - fprintf(out_logfile, "\taggregate filesize = %s\n", - HumanReadable(params->expectedAggFileSize, BASE_TWO)); -#ifdef HAVE_LUSTRE_LUSTRE_USER_H - if (params->lustre_set_striping) { - fprintf(out_logfile, "\tLustre stripe size = %s\n", - ((params->lustre_stripe_size == 0) ? "Use default" : - HumanReadable(params->lustre_stripe_size, BASE_TWO))); - if (params->lustre_stripe_count == 0) { - fprintf(out_logfile, "\t stripe count = %s\n", "Use default"); - } else { - fprintf(out_logfile, "\t stripe count = %d\n", - params->lustre_stripe_count); - } - } -#endif /* HAVE_LUSTRE_LUSTRE_USER_H */ - if (params->deadlineForStonewalling > 0) { - fprintf(out_logfile, "\tUsing stonewalling = %d second(s)%s\n", - params->deadlineForStonewalling, params->stoneWallingWearOut ? " with phase out" : ""); - } - fflush(out_logfile); -} - -/* - * Show test description. - */ -static void ShowTest(IOR_param_t * test) -{ - const char* data_packets[] = {"g", "t","o","i"}; - - fprintf(out_logfile, "TEST:\t%s=%d\n", "id", test->id); - fprintf(out_logfile, "\t%s=%d\n", "refnum", test->referenceNumber); - fprintf(out_logfile, "\t%s=%s\n", "api", test->api); - fprintf(out_logfile, "\t%s=%s\n", "platform", test->platform); - fprintf(out_logfile, "\t%s=%s\n", "testFileName", test->testFileName); - fprintf(out_logfile, "\t%s=%s\n", "hintsFileName", test->hintsFileName); - fprintf(out_logfile, "\t%s=%d\n", "deadlineForStonewall", - test->deadlineForStonewalling); - fprintf(out_logfile, "\t%s=%d\n", "stoneWallingWearOut", test->stoneWallingWearOut); - fprintf(out_logfile, "\t%s=%d\n", "maxTimeDuration", test->maxTimeDuration); - fprintf(out_logfile, "\t%s=%d\n", "outlierThreshold", - test->outlierThreshold); - fprintf(out_logfile, "\t%s=%s\n", "options", test->options); - fprintf(out_logfile, "\t%s=%d\n", "nodes", test->nodes); - fprintf(out_logfile, "\t%s=%lu\n", "memoryPerTask", (unsigned long) test->memoryPerTask); - fprintf(out_logfile, "\t%s=%lu\n", "memoryPerNode", (unsigned long) test->memoryPerNode); - fprintf(out_logfile, "\t%s=%d\n", "tasksPerNode", tasksPerNode); - fprintf(out_logfile, "\t%s=%d\n", "repetitions", test->repetitions); - fprintf(out_logfile, "\t%s=%d\n", "multiFile", test->multiFile); - fprintf(out_logfile, "\t%s=%d\n", "interTestDelay", test->interTestDelay); - fprintf(out_logfile, "\t%s=%d\n", "fsync", test->fsync); - fprintf(out_logfile, "\t%s=%d\n", "fsYncperwrite", test->fsyncPerWrite); - fprintf(out_logfile, "\t%s=%d\n", "useExistingTestFile", - test->useExistingTestFile); - fprintf(out_logfile, "\t%s=%d\n", "showHints", test->showHints); - fprintf(out_logfile, "\t%s=%d\n", "uniqueDir", test->uniqueDir); - fprintf(out_logfile, "\t%s=%d\n", "showHelp", test->showHelp); - fprintf(out_logfile, "\t%s=%d\n", "individualDataSets", - test->individualDataSets); - fprintf(out_logfile, "\t%s=%d\n", "singleXferAttempt", - test->singleXferAttempt); - fprintf(out_logfile, "\t%s=%d\n", "readFile", test->readFile); - fprintf(out_logfile, "\t%s=%d\n", "writeFile", test->writeFile); - fprintf(out_logfile, "\t%s=%d\n", "filePerProc", test->filePerProc); - fprintf(out_logfile, "\t%s=%d\n", "reorderTasks", test->reorderTasks); - fprintf(out_logfile, "\t%s=%d\n", "reorderTasksRandom", - test->reorderTasksRandom); - fprintf(out_logfile, "\t%s=%d\n", "reorderTasksRandomSeed", - test->reorderTasksRandomSeed); - fprintf(out_logfile, "\t%s=%d\n", "randomOffset", test->randomOffset); - fprintf(out_logfile, "\t%s=%d\n", "checkWrite", test->checkWrite); - fprintf(out_logfile, "\t%s=%d\n", "checkRead", test->checkRead); - fprintf(out_logfile, "\t%s=%d\n", "preallocate", test->preallocate); - fprintf(out_logfile, "\t%s=%d\n", "useFileView", test->useFileView); - fprintf(out_logfile, "\t%s=%lld\n", "setAlignment", test->setAlignment); - fprintf(out_logfile, "\t%s=%d\n", "storeFileOffset", test->storeFileOffset); - fprintf(out_logfile, "\t%s=%d\n", "useSharedFilePointer", - test->useSharedFilePointer); - fprintf(out_logfile, "\t%s=%d\n", "useO_DIRECT", test->useO_DIRECT); - fprintf(out_logfile, "\t%s=%d\n", "useStridedDatatype", - test->useStridedDatatype); - fprintf(out_logfile, "\t%s=%d\n", "keepFile", test->keepFile); - fprintf(out_logfile, "\t%s=%d\n", "keepFileWithError", - test->keepFileWithError); - fprintf(out_logfile, "\t%s=%d\n", "quitOnError", test->quitOnError); - fprintf(out_logfile, "\t%s=%d\n", "verbose", verbose); - fprintf(out_logfile, "\t%s=%s\n", "data packet type", data_packets[test->dataPacketType]); - fprintf(out_logfile, "\t%s=%d\n", "setTimeStampSignature/incompressibleSeed", - test->setTimeStampSignature); /* Seed value was copied into setTimeStampSignature as well */ - fprintf(out_logfile, "\t%s=%d\n", "collective", test->collective); - fprintf(out_logfile, "\t%s=%lld", "segmentCount", test->segmentCount); -#ifdef HAVE_GPFS_FCNTL_H - fprintf(out_logfile, "\t%s=%d\n", "gpfsHintAccess", test->gpfs_hint_access); - fprintf(out_logfile, "\t%s=%d\n", "gpfsReleaseToken", test->gpfs_release_token); -#endif - if (strcasecmp(test->api, "HDF5") == 0) { - fprintf(out_logfile, " (datasets)"); - } - fprintf(out_logfile, "\n"); - fprintf(out_logfile, "\t%s=%lld\n", "transferSize", test->transferSize); - fprintf(out_logfile, "\t%s=%lld\n", "blockSize", test->blockSize); -} - -static double mean_of_array_of_doubles(double *values, int len) -{ - double tot = 0.0; - int i; - - for (i = 0; i < len; i++) { - tot += values[i]; - } - return tot / len; - -} - -struct results { - double min; - double max; - double mean; - double var; - double sd; - double sum; - double *val; -}; - -static struct results *bw_values(int reps, IOR_offset_t *agg_file_size, double *vals) -{ - struct results *r; - int i; - - r = (struct results *)malloc(sizeof(struct results) - + (reps * sizeof(double))); - if (r == NULL) - ERR("malloc failed"); - r->val = (double *)&r[1]; - - for (i = 0; i < reps; i++) { - r->val[i] = (double)agg_file_size[i] / vals[i]; - if (i == 0) { - r->min = r->val[i]; - r->max = r->val[i]; - r->sum = 0.0; - } - r->min = MIN(r->min, r->val[i]); - r->max = MAX(r->max, r->val[i]); - r->sum += r->val[i]; - } - r->mean = r->sum / reps; - r->var = 0.0; - for (i = 0; i < reps; i++) { - r->var += pow((r->mean - r->val[i]), 2); - } - r->var = r->var / reps; - r->sd = sqrt(r->var); - - return r; -} - -static struct results *ops_values(int reps, IOR_offset_t *agg_file_size, - IOR_offset_t transfer_size, - double *vals) -{ - struct results *r; - int i; - - r = (struct results *)malloc(sizeof(struct results) - + (reps * sizeof(double))); - if (r == NULL) - ERR("malloc failed"); - r->val = (double *)&r[1]; - - for (i = 0; i < reps; i++) { - r->val[i] = (double)agg_file_size[i] / transfer_size / vals[i]; - if (i == 0) { - r->min = r->val[i]; - r->max = r->val[i]; - r->sum = 0.0; - } - r->min = MIN(r->min, r->val[i]); - r->max = MAX(r->max, r->val[i]); - r->sum += r->val[i]; - } - r->mean = r->sum / reps; - r->var = 0.0; - for (i = 0; i < reps; i++) { - r->var += pow((r->mean - r->val[i]), 2); - } - r->var = r->var / reps; - r->sd = sqrt(r->var); - - return r; -} - -/* - * Summarize results - * - * operation is typically "write" or "read" - */ -static void PrintLongSummaryOneOperation(IOR_test_t *test, double *times, char *operation) -{ - IOR_param_t *params = &test->params; - IOR_results_t *results = test->results; - struct results *bw; - struct results *ops; - int reps; - if (rank != 0 || verbose < VERBOSE_0) - return; - - reps = params->repetitions; - - bw = bw_values(reps, results->aggFileSizeForBW, times); - ops = ops_values(reps, results->aggFileSizeForBW, - params->transferSize, times); - - fprintf(out_logfile, "%-9s ", operation); - fprintf(out_logfile, "%10.2f ", bw->max / MEBIBYTE); - fprintf(out_logfile, "%10.2f ", bw->min / MEBIBYTE); - fprintf(out_logfile, "%10.2f ", bw->mean / MEBIBYTE); - fprintf(out_logfile, "%10.2f ", bw->sd / MEBIBYTE); - fprintf(out_logfile, "%10.2f ", ops->max); - fprintf(out_logfile, "%10.2f ", ops->min); - fprintf(out_logfile, "%10.2f ", ops->mean); - fprintf(out_logfile, "%10.2f ", ops->sd); - fprintf(out_logfile, "%10.5f ", mean_of_array_of_doubles(times, reps)); - fprintf(out_logfile, "%5d ", params->id); - fprintf(out_logfile, "%6d ", params->numTasks); - fprintf(out_logfile, "%3d ", params->tasksPerNode); - fprintf(out_logfile, "%4d ", params->repetitions); - fprintf(out_logfile, "%3d ", params->filePerProc); - fprintf(out_logfile, "%5d ", params->reorderTasks); - fprintf(out_logfile, "%8d ", params->taskPerNodeOffset); - fprintf(out_logfile, "%9d ", params->reorderTasksRandom); - fprintf(out_logfile, "%4d ", params->reorderTasksRandomSeed); - fprintf(out_logfile, "%6lld ", params->segmentCount); - fprintf(out_logfile, "%8lld ", params->blockSize); - fprintf(out_logfile, "%8lld ", params->transferSize); - fprintf(out_logfile, "%9.1f ", (float)results->aggFileSizeForBW[0] / MEBIBYTE); - fprintf(out_logfile, "%3s ", params->api); - fprintf(out_logfile, "%6d", params->referenceNumber); - fprintf(out_logfile, "\n"); - fflush(out_logfile); - - free(bw); - free(ops); -} - -static void PrintLongSummaryOneTest(IOR_test_t *test) -{ - IOR_param_t *params = &test->params; - IOR_results_t *results = test->results; - - if (params->writeFile) - PrintLongSummaryOneOperation(test, results->writeTime, "write"); - if (params->readFile) - PrintLongSummaryOneOperation(test, results->readTime, "read"); -} - -static void PrintLongSummaryHeader() -{ - if (rank != 0 || verbose < VERBOSE_0) - return; - - fprintf(out_logfile, "\n"); - fprintf(out_logfile, "%-9s %10s %10s %10s %10s %10s %10s %10s %10s %10s", - "Operation", "Max(MiB)", "Min(MiB)", "Mean(MiB)", "StdDev", - "Max(OPs)", "Min(OPs)", "Mean(OPs)", "StdDev", - "Mean(s)"); - fprintf(out_logfile, " Test# #Tasks tPN reps fPP reord reordoff reordrand seed" - " segcnt "); - fprintf(out_logfile, "%8s %8s %9s %5s", " blksiz", "xsize","aggs(MiB)", "API"); - fprintf(out_logfile, " RefNum\n"); -} - -static void PrintLongSummaryAllTests(IOR_test_t *tests_head) -{ - IOR_test_t *tptr; - if (rank != 0 || verbose < VERBOSE_0) - return; - - fprintf(out_logfile, "\n"); - fprintf(out_logfile, "Summary of all tests:"); - PrintLongSummaryHeader(); - - for (tptr = tests_head; tptr != NULL; tptr = tptr->next) { - PrintLongSummaryOneTest(tptr); - } -} - -static void PrintShortSummary(IOR_test_t * test) -{ - IOR_param_t *params = &test->params; - IOR_results_t *results = test->results; - double max_write = 0.0; - double max_read = 0.0; - double bw; - int reps; - int i; - - if (rank != 0 || verbose < VERBOSE_0) - return; - - reps = params->repetitions; - - max_write = results->writeTime[0]; - max_read = results->readTime[0]; - for (i = 0; i < reps; i++) { - bw = (double)results->aggFileSizeForBW[i]/results->writeTime[i]; - max_write = MAX(bw, max_write); - bw = (double)results->aggFileSizeForBW[i]/results->readTime[i]; - max_read = MAX(bw, max_read); - } - - fprintf(out_logfile, "\n"); - if (params->writeFile) { - fprintf(out_logfile, "Max Write: %.2f MiB/sec (%.2f MB/sec)\n", - max_write/MEBIBYTE, max_write/MEGABYTE); - } - if (params->readFile) { - fprintf(out_logfile, "Max Read: %.2f MiB/sec (%.2f MB/sec)\n", - max_read/MEBIBYTE, max_read/MEGABYTE); - } -} /* * malloc a buffer, touching every page in an attempt to defeat lazy allocation. diff --git a/src/parse_options.c b/src/parse_options.c index c58b735..6c0d881 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -175,6 +175,7 @@ void DecodeDirective(char *line, IOR_param_t *params) if (out_logfile == NULL){ FAIL("Cannot open output file for writes!"); } + printf("Writing output to %s\n", value); } else if (strcasecmp(option, "summaryFormat") == 0) { if(strcasecmp(value, "default")){ outputFormat = OUTPUT_DEFAULT; diff --git a/src/utilities.c b/src/utilities.c index a1dd25f..623d36d 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -600,3 +600,59 @@ void DelaySecs(int delay){ sleep(delay); } } + + +/* + * Convert IOR_offset_t value to human readable string. This routine uses a + * statically-allocated buffer internally and so is not re-entrant. + */ +char *HumanReadable(IOR_offset_t value, int base) +{ + static char valueStr[MAX_STR]; + IOR_offset_t m = 0, g = 0, t = 0; + char m_str[8], g_str[8], t_str[8]; + + if (base == BASE_TWO) { + m = MEBIBYTE; + g = GIBIBYTE; + t = GIBIBYTE * 1024llu; + strcpy(m_str, "MiB"); + strcpy(g_str, "GiB"); + strcpy(t_str, "TiB"); + } else if (base == BASE_TEN) { + m = MEGABYTE; + g = GIGABYTE; + t = GIGABYTE * 1000llu; + strcpy(m_str, "MB"); + strcpy(g_str, "GB"); + strcpy(t_str, "TB"); + } + + if (value >= t) { + if (value % t) { + snprintf(valueStr, MAX_STR-1, "%.2f %s", + (double)((double)value / t), t_str); + } else { + snprintf(valueStr, MAX_STR-1, "%d %s", (int)(value / t), t_str); + } + }else if (value >= g) { + if (value % g) { + snprintf(valueStr, MAX_STR-1, "%.2f %s", + (double)((double)value / g), g_str); + } else { + snprintf(valueStr, MAX_STR-1, "%d %s", (int)(value / g), g_str); + } + } else if (value >= m) { + if (value % m) { + snprintf(valueStr, MAX_STR-1, "%.2f %s", + (double)((double)value / m), m_str); + } else { + snprintf(valueStr, MAX_STR-1, "%d %s", (int)(value / m), m_str); + } + } else if (value >= 0) { + snprintf(valueStr, MAX_STR-1, "%d bytes", (int)value); + } else { + snprintf(valueStr, MAX_STR-1, "-"); + } + return valueStr; +} diff --git a/src/utilities.h b/src/utilities.h index 21a7dfb..941a066 100755 --- a/src/utilities.h +++ b/src/utilities.h @@ -61,6 +61,7 @@ void DumpBuffer(void *, size_t); void SeedRandGen(MPI_Comm); void SetHints (MPI_Info *, char *); void ShowHints (MPI_Info *); +char *HumanReadable(IOR_offset_t value, int base); int CountTasksPerNode(MPI_Comm comm); void DelaySecs(int delay); From 8525f9734cc346e95a3b9c91a8f7441309e622bf Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sun, 8 Jul 2018 13:47:55 +0100 Subject: [PATCH 42/80] Extraction of output completed. --- src/ior-internal.h | 2 + src/ior-output.c | 313 +++++++++++++++++++++++--------------------- src/ior.c | 11 +- src/parse_options.c | 4 +- src/utilities.c | 1 + src/utilities.h | 1 + 6 files changed, 174 insertions(+), 158 deletions(-) diff --git a/src/ior-internal.h b/src/ior-internal.h index 40b1706..afeeb60 100644 --- a/src/ior-internal.h +++ b/src/ior-internal.h @@ -19,6 +19,8 @@ void DisplayFreespace(IOR_param_t * test); void GetTestFileName(char *, IOR_param_t *); void PrintRemoveTiming(double start, double finish, int rep); void PrintReducedResult(IOR_test_t *test, int access, double bw, double *diff_subset, double totalTime, int rep); +void PrintTestEnds(); +void PrintTableHeader(); /* End of ior-output */ struct results { diff --git a/src/ior-output.c b/src/ior-output.c index 26f189c..ed9fcd3 100644 --- a/src/ior-output.c +++ b/src/ior-output.c @@ -15,8 +15,19 @@ static struct results *ops_values(int reps, IOR_offset_t *agg_file_size, IOR_off static double mean_of_array_of_doubles(double *values, int len); static void PPDouble(int leftjustify, double number, char *append); +void PrintTableHeader(){ + fprintf(out_resultfile, "\n"); + fprintf(out_resultfile, "access bw(MiB/s) block(KiB) xfer(KiB) open(s) wr/rd(s) close(s) total(s) iter\n"); + fprintf(out_resultfile, "------ --------- ---------- --------- -------- -------- -------- -------- ----\n"); +} + +void PrintTestEnds(){ + fprintf(out_resultfile, "\n"); + fprintf(out_resultfile, "Finished: %s", CurrentTimeString()); +} + void PrintReducedResult(IOR_test_t *test, int access, double bw, double *diff_subset, double totalTime, int rep){ - fprintf(out_logfile, "%-10s", access == WRITE ? "write" : "read"); + fprintf(out_resultfile, "%-10s", access == WRITE ? "write" : "read"); PPDouble(1, bw / MEBIBYTE, " "); PPDouble(1, (double)test->params.blockSize / KIBIBYTE, " "); PPDouble(1, (double)test->params.transferSize / KIBIBYTE, " "); @@ -24,8 +35,8 @@ void PrintReducedResult(IOR_test_t *test, int access, double bw, double *diff_su PPDouble(1, diff_subset[1], " "); PPDouble(1, diff_subset[2], " "); PPDouble(1, totalTime, " "); - fprintf(out_logfile, "%-4d\n", rep); - fflush(out_logfile); + fprintf(out_resultfile, "%-4d\n", rep); + fflush(out_resultfile); } @@ -38,8 +49,8 @@ void PrintEarlyHeader() if (rank != 0) return; - fprintf(out_logfile, "IOR-" META_VERSION ": MPI Coordinated Test of Parallel I/O\n"); - fflush(out_logfile); + fprintf(out_resultfile, "IOR-" META_VERSION ": MPI Coordinated Test of Parallel I/O\n"); + fflush(out_resultfile); } void PrintHeader(int argc, char **argv) @@ -50,48 +61,48 @@ void PrintHeader(int argc, char **argv) if (rank != 0) return; - fprintf(out_logfile, "Began: %s", CurrentTimeString()); - fprintf(out_logfile, "Command line used: %s", argv[0]); + fprintf(out_resultfile, "Began: %s", CurrentTimeString()); + fprintf(out_resultfile, "Command line used: %s", argv[0]); for (i = 1; i < argc; i++) { - fprintf(out_logfile, " \"%s\"", argv[i]); + fprintf(out_resultfile, " \"%s\"", argv[i]); } - fprintf(out_logfile, "\n"); + fprintf(out_resultfile, "\n"); if (uname(&unamebuf) != 0) { EWARN("uname failed"); - fprintf(out_logfile, "Machine: Unknown"); + fprintf(out_resultfile, "Machine: Unknown"); } else { - fprintf(out_logfile, "Machine: %s %s", unamebuf.sysname, + fprintf(out_resultfile, "Machine: %s %s", unamebuf.sysname, unamebuf.nodename); if (verbose >= VERBOSE_2) { - fprintf(out_logfile, " %s %s %s", unamebuf.release, + fprintf(out_resultfile, " %s %s %s", unamebuf.release, unamebuf.version, unamebuf.machine); } } - fprintf(out_logfile, "\n"); + fprintf(out_resultfile, "\n"); #ifdef _NO_MPI_TIMER if (verbose >= VERBOSE_2) - fprintf(out_logfile, "Using unsynchronized POSIX timer\n"); + fprintf(out_resultfile, "Using unsynchronized POSIX timer\n"); #else /* not _NO_MPI_TIMER */ if (MPI_WTIME_IS_GLOBAL) { if (verbose >= VERBOSE_2) - fprintf(out_logfile, "Using synchronized MPI timer\n"); + fprintf(out_resultfile, "Using synchronized MPI timer\n"); } else { if (verbose >= VERBOSE_2) - fprintf(out_logfile, "Using unsynchronized MPI timer\n"); + fprintf(out_resultfile, "Using unsynchronized MPI timer\n"); } #endif /* _NO_MPI_TIMER */ if (verbose >= VERBOSE_1) { - fprintf(out_logfile, "Start time skew across all tasks: %.02f sec\n", + fprintf(out_resultfile, "Start time skew across all tasks: %.02f sec\n", wall_clock_deviation); } if (verbose >= VERBOSE_3) { /* show env */ - fprintf(out_logfile, "STARTING ENVIRON LOOP\n"); + fprintf(out_resultfile, "STARTING ENVIRON LOOP\n"); for (i = 0; environ[i] != NULL; i++) { - fprintf(out_logfile, "%s\n", environ[i]); + fprintf(out_resultfile, "%s\n", environ[i]); } - fprintf(out_logfile, "ENDING ENVIRON LOOP\n"); + fprintf(out_resultfile, "ENDING ENVIRON LOOP\n"); } - fflush(out_logfile); + fflush(out_resultfile); } /* @@ -99,15 +110,15 @@ void PrintHeader(int argc, char **argv) */ void ShowTestInfo(IOR_param_t *params) { - fprintf(out_logfile, "\n"); - fprintf(out_logfile, "Test %d started: %s", params->id, CurrentTimeString()); + fprintf(out_resultfile, "\n"); + fprintf(out_resultfile, "Test %d started: %s", params->id, CurrentTimeString()); if (verbose >= VERBOSE_1) { /* if pvfs2:, then skip */ if (Regex(params->testFileName, "^[a-z][a-z].*:") == 0) { DisplayFreespace(params); } } - fflush(out_logfile); + fflush(out_resultfile); } /* @@ -117,79 +128,79 @@ void ShowSetup(IOR_param_t *params) { if (strcmp(params->debug, "") != 0) { - fprintf(out_logfile, "\n*** DEBUG MODE ***\n"); - fprintf(out_logfile, "*** %s ***\n\n", params->debug); + fprintf(out_resultfile, "\n*** DEBUG MODE ***\n"); + fprintf(out_resultfile, "*** %s ***\n\n", params->debug); } - fprintf(out_logfile, "Summary:\n"); - fprintf(out_logfile, "\tapi = %s\n", params->apiVersion); - fprintf(out_logfile, "\ttest filename = %s\n", params->testFileName); - fprintf(out_logfile, "\taccess = "); - fprintf(out_logfile, params->filePerProc ? "file-per-process" : "single-shared-file"); + fprintf(out_resultfile, "Summary:\n"); + fprintf(out_resultfile, "\tapi = %s\n", params->apiVersion); + fprintf(out_resultfile, "\ttest filename = %s\n", params->testFileName); + fprintf(out_resultfile, "\taccess = "); + fprintf(out_resultfile, params->filePerProc ? "file-per-process" : "single-shared-file"); if (verbose >= VERBOSE_1 && strcmp(params->api, "POSIX") != 0) { - fprintf(out_logfile, params->collective == FALSE ? ", independent" : ", collective"); + fprintf(out_resultfile, params->collective == FALSE ? ", independent" : ", collective"); } - fprintf(out_logfile, "\n"); + fprintf(out_resultfile, "\n"); if (verbose >= VERBOSE_1) { if (params->segmentCount > 1) { - fprintf(out_logfile, + fprintf(out_resultfile, "\tpattern = strided (%d segments)\n", (int)params->segmentCount); } else { - fprintf(out_logfile, + fprintf(out_resultfile, "\tpattern = segmented (1 segment)\n"); } } - fprintf(out_logfile, "\tordering in a file ="); + fprintf(out_resultfile, "\tordering in a file ="); if (params->randomOffset == FALSE) { - fprintf(out_logfile, " sequential offsets\n"); + fprintf(out_resultfile, " sequential offsets\n"); } else { - fprintf(out_logfile, " random offsets\n"); + fprintf(out_resultfile, " random offsets\n"); } - fprintf(out_logfile, "\tordering inter file="); + fprintf(out_resultfile, "\tordering inter file="); if (params->reorderTasks == FALSE && params->reorderTasksRandom == FALSE) { - fprintf(out_logfile, " no tasks offsets\n"); + fprintf(out_resultfile, " no tasks offsets\n"); } if (params->reorderTasks == TRUE) { - fprintf(out_logfile, " constant task offsets = %d\n", + fprintf(out_resultfile, " constant task offsets = %d\n", params->taskPerNodeOffset); } if (params->reorderTasksRandom == TRUE) { - fprintf(out_logfile, " random task offsets >= %d, seed=%d\n", + fprintf(out_resultfile, " random task offsets >= %d, seed=%d\n", params->taskPerNodeOffset, params->reorderTasksRandomSeed); } - fprintf(out_logfile, "\tclients = %d (%d per node)\n", + fprintf(out_resultfile, "\tclients = %d (%d per node)\n", params->numTasks, params->tasksPerNode); if (params->memoryPerTask != 0) - fprintf(out_logfile, "\tmemoryPerTask = %s\n", + fprintf(out_resultfile, "\tmemoryPerTask = %s\n", HumanReadable(params->memoryPerTask, BASE_TWO)); if (params->memoryPerNode != 0) - fprintf(out_logfile, "\tmemoryPerNode = %s\n", + fprintf(out_resultfile, "\tmemoryPerNode = %s\n", HumanReadable(params->memoryPerNode, BASE_TWO)); - fprintf(out_logfile, "\trepetitions = %d\n", params->repetitions); - fprintf(out_logfile, "\txfersize = %s\n", + fprintf(out_resultfile, "\trepetitions = %d\n", params->repetitions); + fprintf(out_resultfile, "\txfersize = %s\n", HumanReadable(params->transferSize, BASE_TWO)); - fprintf(out_logfile, "\tblocksize = %s\n", + fprintf(out_resultfile, "\tblocksize = %s\n", HumanReadable(params->blockSize, BASE_TWO)); - fprintf(out_logfile, "\taggregate filesize = %s\n", + fprintf(out_resultfile, "\taggregate filesize = %s\n", HumanReadable(params->expectedAggFileSize, BASE_TWO)); #ifdef HAVE_LUSTRE_LUSTRE_USER_H if (params->lustre_set_striping) { - fprintf(out_logfile, "\tLustre stripe size = %s\n", + fprintf(out_resultfile, "\tLustre stripe size = %s\n", ((params->lustre_stripe_size == 0) ? "Use default" : HumanReadable(params->lustre_stripe_size, BASE_TWO))); if (params->lustre_stripe_count == 0) { - fprintf(out_logfile, "\t stripe count = %s\n", "Use default"); + fprintf(out_resultfile, "\t stripe count = %s\n", "Use default"); } else { - fprintf(out_logfile, "\t stripe count = %d\n", + fprintf(out_resultfile, "\t stripe count = %d\n", params->lustre_stripe_count); } } #endif /* HAVE_LUSTRE_LUSTRE_USER_H */ if (params->deadlineForStonewalling > 0) { - fprintf(out_logfile, "\tUsing stonewalling = %d second(s)%s\n", + fprintf(out_resultfile, "\tUsing stonewalling = %d second(s)%s\n", params->deadlineForStonewalling, params->stoneWallingWearOut ? " with phase out" : ""); } - fflush(out_logfile); + fflush(out_resultfile); } /* @@ -199,77 +210,77 @@ void ShowTest(IOR_param_t * test) { const char* data_packets[] = {"g", "t","o","i"}; - fprintf(out_logfile, "TEST:\t%s=%d\n", "id", test->id); - fprintf(out_logfile, "\t%s=%d\n", "refnum", test->referenceNumber); - fprintf(out_logfile, "\t%s=%s\n", "api", test->api); - fprintf(out_logfile, "\t%s=%s\n", "platform", test->platform); - fprintf(out_logfile, "\t%s=%s\n", "testFileName", test->testFileName); - fprintf(out_logfile, "\t%s=%s\n", "hintsFileName", test->hintsFileName); - fprintf(out_logfile, "\t%s=%d\n", "deadlineForStonewall", + fprintf(out_resultfile, "TEST:\t%s=%d\n", "id", test->id); + fprintf(out_resultfile, "\t%s=%d\n", "refnum", test->referenceNumber); + fprintf(out_resultfile, "\t%s=%s\n", "api", test->api); + fprintf(out_resultfile, "\t%s=%s\n", "platform", test->platform); + fprintf(out_resultfile, "\t%s=%s\n", "testFileName", test->testFileName); + fprintf(out_resultfile, "\t%s=%s\n", "hintsFileName", test->hintsFileName); + fprintf(out_resultfile, "\t%s=%d\n", "deadlineForStonewall", test->deadlineForStonewalling); - fprintf(out_logfile, "\t%s=%d\n", "stoneWallingWearOut", test->stoneWallingWearOut); - fprintf(out_logfile, "\t%s=%d\n", "maxTimeDuration", test->maxTimeDuration); - fprintf(out_logfile, "\t%s=%d\n", "outlierThreshold", + fprintf(out_resultfile, "\t%s=%d\n", "stoneWallingWearOut", test->stoneWallingWearOut); + fprintf(out_resultfile, "\t%s=%d\n", "maxTimeDuration", test->maxTimeDuration); + fprintf(out_resultfile, "\t%s=%d\n", "outlierThreshold", test->outlierThreshold); - fprintf(out_logfile, "\t%s=%s\n", "options", test->options); - fprintf(out_logfile, "\t%s=%d\n", "nodes", test->nodes); - fprintf(out_logfile, "\t%s=%lu\n", "memoryPerTask", (unsigned long) test->memoryPerTask); - fprintf(out_logfile, "\t%s=%lu\n", "memoryPerNode", (unsigned long) test->memoryPerNode); - fprintf(out_logfile, "\t%s=%d\n", "tasksPerNode", tasksPerNode); - fprintf(out_logfile, "\t%s=%d\n", "repetitions", test->repetitions); - fprintf(out_logfile, "\t%s=%d\n", "multiFile", test->multiFile); - fprintf(out_logfile, "\t%s=%d\n", "interTestDelay", test->interTestDelay); - fprintf(out_logfile, "\t%s=%d\n", "fsync", test->fsync); - fprintf(out_logfile, "\t%s=%d\n", "fsYncperwrite", test->fsyncPerWrite); - fprintf(out_logfile, "\t%s=%d\n", "useExistingTestFile", + fprintf(out_resultfile, "\t%s=%s\n", "options", test->options); + fprintf(out_resultfile, "\t%s=%d\n", "nodes", test->nodes); + fprintf(out_resultfile, "\t%s=%lu\n", "memoryPerTask", (unsigned long) test->memoryPerTask); + fprintf(out_resultfile, "\t%s=%lu\n", "memoryPerNode", (unsigned long) test->memoryPerNode); + fprintf(out_resultfile, "\t%s=%d\n", "tasksPerNode", tasksPerNode); + fprintf(out_resultfile, "\t%s=%d\n", "repetitions", test->repetitions); + fprintf(out_resultfile, "\t%s=%d\n", "multiFile", test->multiFile); + fprintf(out_resultfile, "\t%s=%d\n", "interTestDelay", test->interTestDelay); + fprintf(out_resultfile, "\t%s=%d\n", "fsync", test->fsync); + fprintf(out_resultfile, "\t%s=%d\n", "fsYncperwrite", test->fsyncPerWrite); + fprintf(out_resultfile, "\t%s=%d\n", "useExistingTestFile", test->useExistingTestFile); - fprintf(out_logfile, "\t%s=%d\n", "showHints", test->showHints); - fprintf(out_logfile, "\t%s=%d\n", "uniqueDir", test->uniqueDir); - fprintf(out_logfile, "\t%s=%d\n", "showHelp", test->showHelp); - fprintf(out_logfile, "\t%s=%d\n", "individualDataSets", + fprintf(out_resultfile, "\t%s=%d\n", "showHints", test->showHints); + fprintf(out_resultfile, "\t%s=%d\n", "uniqueDir", test->uniqueDir); + fprintf(out_resultfile, "\t%s=%d\n", "showHelp", test->showHelp); + fprintf(out_resultfile, "\t%s=%d\n", "individualDataSets", test->individualDataSets); - fprintf(out_logfile, "\t%s=%d\n", "singleXferAttempt", + fprintf(out_resultfile, "\t%s=%d\n", "singleXferAttempt", test->singleXferAttempt); - fprintf(out_logfile, "\t%s=%d\n", "readFile", test->readFile); - fprintf(out_logfile, "\t%s=%d\n", "writeFile", test->writeFile); - fprintf(out_logfile, "\t%s=%d\n", "filePerProc", test->filePerProc); - fprintf(out_logfile, "\t%s=%d\n", "reorderTasks", test->reorderTasks); - fprintf(out_logfile, "\t%s=%d\n", "reorderTasksRandom", + fprintf(out_resultfile, "\t%s=%d\n", "readFile", test->readFile); + fprintf(out_resultfile, "\t%s=%d\n", "writeFile", test->writeFile); + fprintf(out_resultfile, "\t%s=%d\n", "filePerProc", test->filePerProc); + fprintf(out_resultfile, "\t%s=%d\n", "reorderTasks", test->reorderTasks); + fprintf(out_resultfile, "\t%s=%d\n", "reorderTasksRandom", test->reorderTasksRandom); - fprintf(out_logfile, "\t%s=%d\n", "reorderTasksRandomSeed", + fprintf(out_resultfile, "\t%s=%d\n", "reorderTasksRandomSeed", test->reorderTasksRandomSeed); - fprintf(out_logfile, "\t%s=%d\n", "randomOffset", test->randomOffset); - fprintf(out_logfile, "\t%s=%d\n", "checkWrite", test->checkWrite); - fprintf(out_logfile, "\t%s=%d\n", "checkRead", test->checkRead); - fprintf(out_logfile, "\t%s=%d\n", "preallocate", test->preallocate); - fprintf(out_logfile, "\t%s=%d\n", "useFileView", test->useFileView); - fprintf(out_logfile, "\t%s=%lld\n", "setAlignment", test->setAlignment); - fprintf(out_logfile, "\t%s=%d\n", "storeFileOffset", test->storeFileOffset); - fprintf(out_logfile, "\t%s=%d\n", "useSharedFilePointer", + fprintf(out_resultfile, "\t%s=%d\n", "randomOffset", test->randomOffset); + fprintf(out_resultfile, "\t%s=%d\n", "checkWrite", test->checkWrite); + fprintf(out_resultfile, "\t%s=%d\n", "checkRead", test->checkRead); + fprintf(out_resultfile, "\t%s=%d\n", "preallocate", test->preallocate); + fprintf(out_resultfile, "\t%s=%d\n", "useFileView", test->useFileView); + fprintf(out_resultfile, "\t%s=%lld\n", "setAlignment", test->setAlignment); + fprintf(out_resultfile, "\t%s=%d\n", "storeFileOffset", test->storeFileOffset); + fprintf(out_resultfile, "\t%s=%d\n", "useSharedFilePointer", test->useSharedFilePointer); - fprintf(out_logfile, "\t%s=%d\n", "useO_DIRECT", test->useO_DIRECT); - fprintf(out_logfile, "\t%s=%d\n", "useStridedDatatype", + fprintf(out_resultfile, "\t%s=%d\n", "useO_DIRECT", test->useO_DIRECT); + fprintf(out_resultfile, "\t%s=%d\n", "useStridedDatatype", test->useStridedDatatype); - fprintf(out_logfile, "\t%s=%d\n", "keepFile", test->keepFile); - fprintf(out_logfile, "\t%s=%d\n", "keepFileWithError", + fprintf(out_resultfile, "\t%s=%d\n", "keepFile", test->keepFile); + fprintf(out_resultfile, "\t%s=%d\n", "keepFileWithError", test->keepFileWithError); - fprintf(out_logfile, "\t%s=%d\n", "quitOnError", test->quitOnError); - fprintf(out_logfile, "\t%s=%d\n", "verbose", verbose); - fprintf(out_logfile, "\t%s=%s\n", "data packet type", data_packets[test->dataPacketType]); - fprintf(out_logfile, "\t%s=%d\n", "setTimeStampSignature/incompressibleSeed", + fprintf(out_resultfile, "\t%s=%d\n", "quitOnError", test->quitOnError); + fprintf(out_resultfile, "\t%s=%d\n", "verbose", verbose); + fprintf(out_resultfile, "\t%s=%s\n", "data packet type", data_packets[test->dataPacketType]); + fprintf(out_resultfile, "\t%s=%d\n", "setTimeStampSignature/incompressibleSeed", test->setTimeStampSignature); /* Seed value was copied into setTimeStampSignature as well */ - fprintf(out_logfile, "\t%s=%d\n", "collective", test->collective); - fprintf(out_logfile, "\t%s=%lld", "segmentCount", test->segmentCount); + fprintf(out_resultfile, "\t%s=%d\n", "collective", test->collective); + fprintf(out_resultfile, "\t%s=%lld", "segmentCount", test->segmentCount); #ifdef HAVE_GPFS_FCNTL_H - fprintf(out_logfile, "\t%s=%d\n", "gpfsHintAccess", test->gpfs_hint_access); - fprintf(out_logfile, "\t%s=%d\n", "gpfsReleaseToken", test->gpfs_release_token); + fprintf(out_resultfile, "\t%s=%d\n", "gpfsHintAccess", test->gpfs_hint_access); + fprintf(out_resultfile, "\t%s=%d\n", "gpfsReleaseToken", test->gpfs_release_token); #endif if (strcasecmp(test->api, "HDF5") == 0) { - fprintf(out_logfile, " (datasets)"); + fprintf(out_resultfile, " (datasets)"); } - fprintf(out_logfile, "\n"); - fprintf(out_logfile, "\t%s=%lld\n", "transferSize", test->transferSize); - fprintf(out_logfile, "\t%s=%lld\n", "blockSize", test->blockSize); + fprintf(out_resultfile, "\n"); + fprintf(out_resultfile, "\t%s=%lld\n", "transferSize", test->transferSize); + fprintf(out_resultfile, "\t%s=%lld\n", "blockSize", test->blockSize); } @@ -294,33 +305,33 @@ void PrintLongSummaryOneOperation(IOR_test_t *test, double *times, char *operati ops = ops_values(reps, results->aggFileSizeForBW, params->transferSize, times); - fprintf(out_logfile, "%-9s ", operation); - fprintf(out_logfile, "%10.2f ", bw->max / MEBIBYTE); - fprintf(out_logfile, "%10.2f ", bw->min / MEBIBYTE); - fprintf(out_logfile, "%10.2f ", bw->mean / MEBIBYTE); - fprintf(out_logfile, "%10.2f ", bw->sd / MEBIBYTE); - fprintf(out_logfile, "%10.2f ", ops->max); - fprintf(out_logfile, "%10.2f ", ops->min); - fprintf(out_logfile, "%10.2f ", ops->mean); - fprintf(out_logfile, "%10.2f ", ops->sd); - fprintf(out_logfile, "%10.5f ", mean_of_array_of_doubles(times, reps)); - fprintf(out_logfile, "%5d ", params->id); - fprintf(out_logfile, "%6d ", params->numTasks); - fprintf(out_logfile, "%3d ", params->tasksPerNode); - fprintf(out_logfile, "%4d ", params->repetitions); - fprintf(out_logfile, "%3d ", params->filePerProc); - fprintf(out_logfile, "%5d ", params->reorderTasks); - fprintf(out_logfile, "%8d ", params->taskPerNodeOffset); - fprintf(out_logfile, "%9d ", params->reorderTasksRandom); - fprintf(out_logfile, "%4d ", params->reorderTasksRandomSeed); - fprintf(out_logfile, "%6lld ", params->segmentCount); - fprintf(out_logfile, "%8lld ", params->blockSize); - fprintf(out_logfile, "%8lld ", params->transferSize); - fprintf(out_logfile, "%9.1f ", (float)results->aggFileSizeForBW[0] / MEBIBYTE); - fprintf(out_logfile, "%3s ", params->api); - fprintf(out_logfile, "%6d", params->referenceNumber); - fprintf(out_logfile, "\n"); - fflush(out_logfile); + fprintf(out_resultfile, "%-9s ", operation); + fprintf(out_resultfile, "%10.2f ", bw->max / MEBIBYTE); + fprintf(out_resultfile, "%10.2f ", bw->min / MEBIBYTE); + fprintf(out_resultfile, "%10.2f ", bw->mean / MEBIBYTE); + fprintf(out_resultfile, "%10.2f ", bw->sd / MEBIBYTE); + fprintf(out_resultfile, "%10.2f ", ops->max); + fprintf(out_resultfile, "%10.2f ", ops->min); + fprintf(out_resultfile, "%10.2f ", ops->mean); + fprintf(out_resultfile, "%10.2f ", ops->sd); + fprintf(out_resultfile, "%10.5f ", mean_of_array_of_doubles(times, reps)); + fprintf(out_resultfile, "%5d ", params->id); + fprintf(out_resultfile, "%6d ", params->numTasks); + fprintf(out_resultfile, "%3d ", params->tasksPerNode); + fprintf(out_resultfile, "%4d ", params->repetitions); + fprintf(out_resultfile, "%3d ", params->filePerProc); + fprintf(out_resultfile, "%5d ", params->reorderTasks); + fprintf(out_resultfile, "%8d ", params->taskPerNodeOffset); + fprintf(out_resultfile, "%9d ", params->reorderTasksRandom); + fprintf(out_resultfile, "%4d ", params->reorderTasksRandomSeed); + fprintf(out_resultfile, "%6lld ", params->segmentCount); + fprintf(out_resultfile, "%8lld ", params->blockSize); + fprintf(out_resultfile, "%8lld ", params->transferSize); + fprintf(out_resultfile, "%9.1f ", (float)results->aggFileSizeForBW[0] / MEBIBYTE); + fprintf(out_resultfile, "%3s ", params->api); + fprintf(out_resultfile, "%6d", params->referenceNumber); + fprintf(out_resultfile, "\n"); + fflush(out_resultfile); free(bw); free(ops); @@ -342,15 +353,15 @@ void PrintLongSummaryHeader() if (rank != 0 || verbose < VERBOSE_0) return; - fprintf(out_logfile, "\n"); - fprintf(out_logfile, "%-9s %10s %10s %10s %10s %10s %10s %10s %10s %10s", + fprintf(out_resultfile, "\n"); + fprintf(out_resultfile, "%-9s %10s %10s %10s %10s %10s %10s %10s %10s %10s", "Operation", "Max(MiB)", "Min(MiB)", "Mean(MiB)", "StdDev", "Max(OPs)", "Min(OPs)", "Mean(OPs)", "StdDev", "Mean(s)"); - fprintf(out_logfile, " Test# #Tasks tPN reps fPP reord reordoff reordrand seed" + fprintf(out_resultfile, " Test# #Tasks tPN reps fPP reord reordoff reordrand seed" " segcnt "); - fprintf(out_logfile, "%8s %8s %9s %5s", " blksiz", "xsize","aggs(MiB)", "API"); - fprintf(out_logfile, " RefNum\n"); + fprintf(out_resultfile, "%8s %8s %9s %5s", " blksiz", "xsize","aggs(MiB)", "API"); + fprintf(out_resultfile, " RefNum\n"); } void PrintLongSummaryAllTests(IOR_test_t *tests_head) @@ -359,8 +370,8 @@ void PrintLongSummaryAllTests(IOR_test_t *tests_head) if (rank != 0 || verbose < VERBOSE_0) return; - fprintf(out_logfile, "\n"); - fprintf(out_logfile, "Summary of all tests:"); + fprintf(out_resultfile, "\n"); + fprintf(out_resultfile, "Summary of all tests:"); PrintLongSummaryHeader(); for (tptr = tests_head; tptr != NULL; tptr = tptr->next) { @@ -392,13 +403,13 @@ void PrintShortSummary(IOR_test_t * test) max_read = MAX(bw, max_read); } - fprintf(out_logfile, "\n"); + fprintf(out_resultfile, "\n"); if (params->writeFile) { - fprintf(out_logfile, "Max Write: %.2f MiB/sec (%.2f MB/sec)\n", + fprintf(out_resultfile, "Max Write: %.2f MiB/sec (%.2f MB/sec)\n", max_write/MEBIBYTE, max_write/MEGABYTE); } if (params->readFile) { - fprintf(out_logfile, "Max Read: %.2f MiB/sec (%.2f MB/sec)\n", + fprintf(out_resultfile, "Max Read: %.2f MiB/sec (%.2f MB/sec)\n", max_read/MEBIBYTE, max_read/MEGABYTE); } } @@ -442,9 +453,9 @@ void PrintRemoveTiming(double start, double finish, int rep) if (rank != 0 || verbose < VERBOSE_0) return; - fprintf(out_logfile, "remove - - - - - - "); + fprintf(out_resultfile, "remove - - - - - - "); PPDouble(1, finish-start, " "); - fprintf(out_logfile, "%-4d\n", rep); + fprintf(out_resultfile, "%-4d\n", rep); } @@ -460,7 +471,7 @@ static void PPDouble(int leftjustify, double number, char *append) int precision; if (number < 0) { - fprintf(out_logfile, " - %s", append); + fprintf(out_resultfile, " - %s", append); return; } @@ -475,7 +486,7 @@ static void PPDouble(int leftjustify, double number, char *append) leftjustify ? "-" : "", width, precision); - fprintf(out_logfile, format, number, append); + fprintf(out_resultfile, format, number, append); } diff --git a/src/ior.c b/src/ior.c index 5d2902e..394cb0d 100755 --- a/src/ior.c +++ b/src/ior.c @@ -56,6 +56,7 @@ IOR_test_t * ior_run(int argc, char **argv, MPI_Comm world_com, FILE * world_out IOR_test_t *tests_head; IOR_test_t *tptr; out_logfile = world_out; + out_resultfile = world_out; mpi_comm_world = world_com; MPI_CHECK(MPI_Comm_size(mpi_comm_world, &numTasksWorld), "cannot get number of tasks"); @@ -122,6 +123,7 @@ int ior_main(int argc, char **argv) IOR_test_t *tptr; out_logfile = stdout; + out_resultfile = stdout; /* * check -h option from commandline without starting MPI; @@ -213,8 +215,7 @@ int ior_main(int argc, char **argv) /* display finish time */ if (rank == 0 && verbose >= VERBOSE_0) { - fprintf(out_logfile, "\n"); - fprintf(out_logfile, "Finished: %s", CurrentTimeString()); + PrintTestEnds(); } DestroyTests(tests_head); @@ -730,6 +731,8 @@ static void DisplayUsage(char **argv) " -Y fsyncPerWrite -- perform fsync/msync after each POSIX/MMAP write", " -z randomOffset -- access is to random, not sequential, offsets within a file", " -Z reorderTasksRandom -- changes task ordering to random ordering for readback", + " -O summaryFile=FILE -- store result data into this file", + " -O summaryFormat=[default,JSON,CSV] -- use the format for outputing the summary", " ", " NOTE: S is a string, N is an integer number.", " ", @@ -1407,9 +1410,7 @@ static void TestIoSys(IOR_test_t *test) } } if (rep == 0 && verbose >= VERBOSE_0) { - fprintf(out_logfile, "\n"); - fprintf(out_logfile, "access bw(MiB/s) block(KiB) xfer(KiB) open(s) wr/rd(s) close(s) total(s) iter\n"); - fprintf(out_logfile, "------ --------- ---------- --------- -------- -------- -------- -------- ----\n"); + PrintTableHeader(); } } MPI_CHECK(MPI_Bcast diff --git a/src/parse_options.c b/src/parse_options.c index 6c0d881..cdb7a57 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -171,8 +171,8 @@ void DecodeDirective(char *line, IOR_param_t *params) if (strcasecmp(option, "api") == 0) { strcpy(params->api, value); } else if (strcasecmp(option, "summaryFile") == 0) { - out_logfile = fopen(value, "w"); - if (out_logfile == NULL){ + out_resultfile = fopen(value, "w"); + if (out_resultfile == NULL){ FAIL("Cannot open output file for writes!"); } printf("Writing output to %s\n", value); diff --git a/src/utilities.c b/src/utilities.c index 623d36d..41c996f 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -57,6 +57,7 @@ int verbose = VERBOSE_0; /* verbose output */ MPI_Comm testComm; MPI_Comm mpi_comm_world; FILE * out_logfile; +FILE * out_resultfile; enum OutputFormat_t outputFormat; /***************************** F U N C T I O N S ******************************/ diff --git a/src/utilities.h b/src/utilities.h index 941a066..7933cbc 100755 --- a/src/utilities.h +++ b/src/utilities.h @@ -26,6 +26,7 @@ extern int verbose; extern MPI_Comm testComm; extern MPI_Comm mpi_comm_world; extern FILE * out_logfile; +extern FILE * out_resultfile; extern enum OutputFormat_t outputFormat; /* format of the output */ /* From 20ebeb71b82b4c64fa5fbeb5ab710972621a4d8a Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sun, 8 Jul 2018 14:59:54 +0100 Subject: [PATCH 43/80] Further adaptions to generic output, support of JSON output (partially complete). --- src/ior-internal.h | 1 + src/ior-output.c | 222 +++++++++++++++++++++++++++++++++++--------- src/ior.c | 28 +----- src/parse_options.c | 16 ++-- src/utilities.c | 30 ++++-- 5 files changed, 214 insertions(+), 83 deletions(-) diff --git a/src/ior-internal.h b/src/ior-internal.h index afeeb60..d47fa8c 100644 --- a/src/ior-internal.h +++ b/src/ior-internal.h @@ -9,6 +9,7 @@ void PrintEarlyHeader(); void PrintHeader(int argc, char **argv); void ShowTestInfo(IOR_param_t *params); +void ShowTestEnd(IOR_test_t *tptr); void ShowSetup(IOR_param_t *params); void ShowTest(IOR_param_t * test); void PrintShortSummary(IOR_test_t * test); diff --git a/src/ior-output.c b/src/ior-output.c index ed9fcd3..c5bffae 100644 --- a/src/ior-output.c +++ b/src/ior-output.c @@ -16,14 +16,128 @@ static double mean_of_array_of_doubles(double *values, int len); static void PPDouble(int leftjustify, double number, char *append); void PrintTableHeader(){ - fprintf(out_resultfile, "\n"); - fprintf(out_resultfile, "access bw(MiB/s) block(KiB) xfer(KiB) open(s) wr/rd(s) close(s) total(s) iter\n"); - fprintf(out_resultfile, "------ --------- ---------- --------- -------- -------- -------- -------- ----\n"); + if (outputFormat == OUTPUT_DEFAULT){ + fprintf(out_resultfile, "\n"); + fprintf(out_resultfile, "access bw(MiB/s) block(KiB) xfer(KiB) open(s) wr/rd(s) close(s) total(s) iter\n"); + fprintf(out_resultfile, "------ --------- ---------- --------- -------- -------- -------- -------- ----\n"); + } +} + +static int indent = 0; + +static void PrintKeyValStart(char * key){ + if (outputFormat == OUTPUT_DEFAULT){ + for(int i=0; i < indent; i++){ + fprintf(out_resultfile, " "); + } + fprintf(out_resultfile, "%s: ", key); + return; + } + if(outputFormat == OUTPUT_JSON){ + fprintf(out_resultfile, "\"%s\": \"", key); + }else if(outputFormat == OUTPUT_CSV){ + + } +} + +static void PrintNextToken(){ + if(outputFormat == OUTPUT_JSON){ + fprintf(out_resultfile, ", \n"); + } +} + +static void PrintKeyValEnd(){ + if(outputFormat == OUTPUT_JSON){ + fprintf(out_resultfile, "\""); + } + if (outputFormat == OUTPUT_DEFAULT){ + fprintf(out_resultfile, "\n"); + } +} + +static void PrintIndent(){ + if(outputFormat == OUTPUT_CSV){ + return; + } + for(int i=0; i < indent; i++){ + fprintf(out_resultfile, " "); + } +} + +static void PrintKeyVal(char * key, char * value){ + if(value[strlen(value) -1 ] == '\n'){ + // remove \n + value[strlen(value) -1 ] = 0; + } + PrintIndent(); + if (outputFormat == OUTPUT_DEFAULT){ + fprintf(out_resultfile, "%s: %s\n", key, value); + return; + } + if(outputFormat == OUTPUT_JSON){ + fprintf(out_resultfile, "\"%s\": \"%s\"", key, value); + }else if(outputFormat == OUTPUT_CSV){ + fprintf(out_resultfile, "%s", value); + } +} + +static void PrintKeyValInt(char * key, int64_t value){ + PrintIndent(); + if (outputFormat == OUTPUT_DEFAULT){ + fprintf(out_resultfile, "%s: %lld\n", key, (long long) value); + return; + } + if(outputFormat == OUTPUT_JSON){ + fprintf(out_resultfile, "\"%s\": %lld", key, (long long) value); + }else if(outputFormat == OUTPUT_CSV){ + fprintf(out_resultfile, "%lld", (long long) value); + } +} + +static void PrintStartSection(){ + indent++; + if(outputFormat == OUTPUT_JSON){ + fprintf(out_resultfile, "{\n"); + } +} + +static void PrintNamedSectionStart(char * key){ + PrintIndent(); + indent++; + if(outputFormat == OUTPUT_JSON){ + fprintf(out_resultfile, "\"%s\": {\n", key); + }else if(outputFormat == OUTPUT_DEFAULT){ + fprintf(out_resultfile, "%s: \n", key); + } +} + +static void PrintEndSection(){ + indent--; + if(outputFormat == OUTPUT_JSON){ + fprintf(out_resultfile, "\n}\n"); + } +} + +static void PrintArrayStart(char * key){ + if(outputFormat == OUTPUT_JSON){ + fprintf(out_resultfile, "\"%s\": [\n", key); + } +} + +static void PrintArrayEnd(){ + if(outputFormat == OUTPUT_JSON){ + fprintf(out_resultfile, "]\n"); + } } void PrintTestEnds(){ - fprintf(out_resultfile, "\n"); - fprintf(out_resultfile, "Finished: %s", CurrentTimeString()); + if (rank != 0 || verbose < VERBOSE_0) { + PrintEndSection(); + return; + } + + PrintKeyVal("Finished", CurrentTimeString()); + PrintEndSection(); } void PrintReducedResult(IOR_test_t *test, int access, double bw, double *diff_subset, double totalTime, int rep){ @@ -60,49 +174,59 @@ void PrintHeader(int argc, char **argv) if (rank != 0) return; + PrintStartSection(); - fprintf(out_resultfile, "Began: %s", CurrentTimeString()); - fprintf(out_resultfile, "Command line used: %s", argv[0]); + PrintKeyVal("Began", CurrentTimeString()); + PrintNextToken(); + PrintKeyValStart("Command line"); + fprintf(out_resultfile, "%s", argv[0]); for (i = 1; i < argc; i++) { - fprintf(out_resultfile, " \"%s\"", argv[i]); + fprintf(out_resultfile, " %s", argv[i]); } - fprintf(out_resultfile, "\n"); + PrintKeyValEnd(); + PrintNextToken(); if (uname(&unamebuf) != 0) { EWARN("uname failed"); - fprintf(out_resultfile, "Machine: Unknown"); + PrintKeyVal("Machine", "Unknown"); } else { - fprintf(out_resultfile, "Machine: %s %s", unamebuf.sysname, + PrintKeyValStart("Machine"); + fprintf(out_resultfile, "%s %s", unamebuf.sysname, unamebuf.nodename); if (verbose >= VERBOSE_2) { fprintf(out_resultfile, " %s %s %s", unamebuf.release, unamebuf.version, unamebuf.machine); } + PrintKeyValEnd(); } - fprintf(out_resultfile, "\n"); + #ifdef _NO_MPI_TIMER if (verbose >= VERBOSE_2) - fprintf(out_resultfile, "Using unsynchronized POSIX timer\n"); + fprintf(out_logfile, "Using unsynchronized POSIX timer\n"); #else /* not _NO_MPI_TIMER */ if (MPI_WTIME_IS_GLOBAL) { if (verbose >= VERBOSE_2) - fprintf(out_resultfile, "Using synchronized MPI timer\n"); + fprintf(out_logfile, "Using synchronized MPI timer\n"); } else { if (verbose >= VERBOSE_2) - fprintf(out_resultfile, "Using unsynchronized MPI timer\n"); + fprintf(out_logfile, "Using unsynchronized MPI timer\n"); } #endif /* _NO_MPI_TIMER */ if (verbose >= VERBOSE_1) { - fprintf(out_resultfile, "Start time skew across all tasks: %.02f sec\n", + fprintf(out_logfile, "Start time skew across all tasks: %.02f sec\n", wall_clock_deviation); } if (verbose >= VERBOSE_3) { /* show env */ - fprintf(out_resultfile, "STARTING ENVIRON LOOP\n"); + fprintf(out_logfile, "STARTING ENVIRON LOOP\n"); for (i = 0; environ[i] != NULL; i++) { - fprintf(out_resultfile, "%s\n", environ[i]); + fprintf(out_logfile, "%s\n", environ[i]); } - fprintf(out_resultfile, "ENDING ENVIRON LOOP\n"); + fprintf(out_logfile, "ENDING ENVIRON LOOP\n"); } + + PrintNextToken(); + PrintArrayStart("tests"); fflush(out_resultfile); + fflush(out_logfile); } /* @@ -110,15 +234,27 @@ void PrintHeader(int argc, char **argv) */ void ShowTestInfo(IOR_param_t *params) { - fprintf(out_resultfile, "\n"); - fprintf(out_resultfile, "Test %d started: %s", params->id, CurrentTimeString()); - if (verbose >= VERBOSE_1) { - /* if pvfs2:, then skip */ - if (Regex(params->testFileName, "^[a-z][a-z].*:") == 0) { - DisplayFreespace(params); - } - } - fflush(out_resultfile); + PrintStartSection(); + PrintKeyValInt("TestID", params->id); + PrintNextToken(); + PrintKeyVal("StartTime", CurrentTimeString()); + PrintNextToken(); + /* if pvfs2:, then skip */ + if (Regex(params->testFileName, "^[a-z][a-z].*:") == 0) { + DisplayFreespace(params); + } + fflush(out_resultfile); +} + +void ShowTestEnd(IOR_test_t *tptr){ + if(rank == 0 && tptr->params.stoneWallingWearOut){ + if (tptr->params.stoneWallingStatusFile[0]){ + StoreStoneWallingIterations(tptr->params.stoneWallingStatusFile, tptr->results->pairs_accessed); + }else{ + fprintf(out_logfile, "Pairs deadlineForStonewallingaccessed: %lld\n", (long long) tptr->results->pairs_accessed); + } + } + PrintEndSection(); } /* @@ -126,20 +262,19 @@ void ShowTestInfo(IOR_param_t *params) */ void ShowSetup(IOR_param_t *params) { - - if (strcmp(params->debug, "") != 0) { - fprintf(out_resultfile, "\n*** DEBUG MODE ***\n"); - fprintf(out_resultfile, "*** %s ***\n\n", params->debug); - } - fprintf(out_resultfile, "Summary:\n"); - fprintf(out_resultfile, "\tapi = %s\n", params->apiVersion); - fprintf(out_resultfile, "\ttest filename = %s\n", params->testFileName); - fprintf(out_resultfile, "\taccess = "); - fprintf(out_resultfile, params->filePerProc ? "file-per-process" : "single-shared-file"); - if (verbose >= VERBOSE_1 && strcmp(params->api, "POSIX") != 0) { - fprintf(out_resultfile, params->collective == FALSE ? ", independent" : ", collective"); - } - fprintf(out_resultfile, "\n"); + if (strcmp(params->debug, "") != 0) { + fprintf(out_logfile, "\n*** DEBUG MODE ***\n"); + fprintf(out_logfile, "*** %s ***\n\n", params->debug); + } + PrintNamedSectionStart("Flags"); + PrintKeyVal("api", params->apiVersion); + PrintNextToken(); + PrintKeyVal("test filename", params->testFileName); + PrintNextToken(); + PrintKeyVal("access", params->filePerProc ? "file-per-process" : "single-shared-file"); + PrintNextToken(); + PrintKeyVal("type", params->collective == FALSE ? "independent" : "collective"); + PrintNextToken(); if (verbose >= VERBOSE_1) { if (params->segmentCount > 1) { fprintf(out_resultfile, @@ -200,6 +335,7 @@ void ShowSetup(IOR_param_t *params) fprintf(out_resultfile, "\tUsing stonewalling = %d second(s)%s\n", params->deadlineForStonewalling, params->stoneWallingWearOut ? " with phase out" : ""); } + PrintEndSection(); fflush(out_resultfile); } @@ -443,8 +579,6 @@ void DisplayFreespace(IOR_param_t * test) } ShowFileSystemSize(fileName); - - return; } diff --git a/src/ior.c b/src/ior.c index 394cb0d..3ece7e6 100755 --- a/src/ior.c +++ b/src/ior.c @@ -94,23 +94,14 @@ IOR_test_t * ior_run(int argc, char **argv, MPI_Comm world_com, FILE * world_out } TestIoSys(tptr); - if(rank == 0 && tptr->params.stoneWallingWearOut){ - if (tptr->params.stoneWallingStatusFile[0]){ - StoreStoneWallingIterations(tptr->params.stoneWallingStatusFile, tptr->results->pairs_accessed); - }else{ - fprintf(out_logfile, "Pairs deadlineForStonewallingaccessed: %lld\n", (long long) tptr->results->pairs_accessed); - } - } tptr->results->errors = totalErrorCount; + ShowTestEnd(tptr); } PrintLongSummaryAllTests(tests_head); /* display finish time */ - if (rank == 0 && verbose >= VERBOSE_0) { - fprintf(out_logfile, "\n"); - fprintf(out_logfile, "Finished: %s", CurrentTimeString()); - } + PrintTestEnds(); return tests_head; } @@ -198,14 +189,7 @@ int ior_main(int argc, char **argv) fprintf(out_logfile, "\trank %d: awake.\n", rank); } TestIoSys(tptr); - - if(rank == 0 && tptr->params.stoneWallingWearOut){ - if (tptr->params.stoneWallingStatusFile[0]){ - StoreStoneWallingIterations(tptr->params.stoneWallingStatusFile, tptr->results->pairs_accessed); - }else{ - fprintf(out_logfile, "Pairs deadlineForStonewallingaccessed: %lld\n", (long long) tptr->results->pairs_accessed); - } - } + ShowTestEnd(tptr); } if (verbose < 0) @@ -214,9 +198,7 @@ int ior_main(int argc, char **argv) PrintLongSummaryAllTests(tests_head); /* display finish time */ - if (rank == 0 && verbose >= VERBOSE_0) { - PrintTestEnds(); - } + PrintTestEnds(); DestroyTests(tests_head); @@ -228,8 +210,6 @@ int ior_main(int argc, char **argv) aws_cleanup(); #endif - fflush(out_logfile); - return totalErrorCount; } diff --git a/src/parse_options.c b/src/parse_options.c index cdb7a57..4696727 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -171,17 +171,19 @@ void DecodeDirective(char *line, IOR_param_t *params) if (strcasecmp(option, "api") == 0) { strcpy(params->api, value); } else if (strcasecmp(option, "summaryFile") == 0) { - out_resultfile = fopen(value, "w"); - if (out_resultfile == NULL){ - FAIL("Cannot open output file for writes!"); + if (rank == 0){ + out_resultfile = fopen(value, "w"); + if (out_resultfile == NULL){ + FAIL("Cannot open output file for writes!"); + } + printf("Writing output to %s\n", value); } - printf("Writing output to %s\n", value); } else if (strcasecmp(option, "summaryFormat") == 0) { - if(strcasecmp(value, "default")){ + if(strcasecmp(value, "default") == 0){ outputFormat = OUTPUT_DEFAULT; - }else if(strcasecmp(value, "JSON")){ + }else if(strcasecmp(value, "JSON") == 0){ outputFormat = OUTPUT_JSON; - }else if(strcasecmp(value, "CSV")){ + }else if(strcasecmp(value, "CSV") == 0){ outputFormat = OUTPUT_CSV; }else{ FAIL("Unknown summaryFormat"); diff --git a/src/utilities.c b/src/utilities.c index 41c996f..dd20177 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -406,14 +406,28 @@ void ShowFileSystemSize(char *fileSystem) if (realpath(fileSystem, realPath) == NULL) { ERR("unable to use realpath()"); } - fprintf(out_logfile, "Path: %s\n", realPath); - fprintf(out_logfile, "FS: %.1f %s Used FS: %2.1f%% ", - totalFileSystemSizeHR, fileSystemUnitStr, - usedFileSystemPercentage); - fprintf(out_logfile, "Inodes: %.1f Mi Used Inodes: %2.1f%%\n", - (double)totalInodes / (double)(1<<20), - usedInodePercentage); - fflush(out_logfile); + + if(outputFormat == OUTPUT_DEFAULT){ + fprintf(out_resultfile, "Path: %s\n", realPath); + fprintf(out_resultfile, "FS: %.1f %s Used FS: %2.1f%% ", + totalFileSystemSizeHR, fileSystemUnitStr, + usedFileSystemPercentage); + fprintf(out_resultfile, "Inodes: %.1f Mi Used Inodes: %2.1f%%\n", + (double)totalInodes / (double)(1<<20), + usedInodePercentage); + fflush(out_logfile); + }else if(outputFormat == OUTPUT_JSON){ + fprintf(out_resultfile, " \"Path\": \"%s\",", realPath); + fprintf(out_resultfile, "\"Capacity\": \"%.1f %s\", \"Used Capacity\": \"%2.1f%%\",", + totalFileSystemSizeHR, fileSystemUnitStr, + usedFileSystemPercentage); + fprintf(out_resultfile, "\"Inodes\": \"%.1f Mi\", \"Used Inodes\" : \"%2.1f%%\",\n", + (double)totalInodes / (double)(1<<20), + usedInodePercentage); + }else if(outputFormat == OUTPUT_CSV){ + + } + #endif /* !_WIN32 */ return; From 2bc30410cc02fa214f9bcd6d544320eb99a0cf14 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sun, 8 Jul 2018 16:47:23 +0100 Subject: [PATCH 44/80] JSON output works. --- src/ior-internal.h | 6 +- src/ior-output.c | 547 ++++++++++++++++++++++++++------------------- src/ior.c | 15 +- src/utilities.c | 4 +- 4 files changed, 332 insertions(+), 240 deletions(-) diff --git a/src/ior-internal.h b/src/ior-internal.h index d47fa8c..11bbb8d 100644 --- a/src/ior-internal.h +++ b/src/ior-internal.h @@ -8,10 +8,12 @@ /* Part of ior-output.c */ void PrintEarlyHeader(); void PrintHeader(int argc, char **argv); -void ShowTestInfo(IOR_param_t *params); +void ShowTestStart(IOR_param_t *params); void ShowTestEnd(IOR_test_t *tptr); void ShowSetup(IOR_param_t *params); -void ShowTest(IOR_param_t * test); +void PrintRepeatEnd(); +void PrintRepeatStart(); + void PrintShortSummary(IOR_test_t * test); void PrintLongSummaryAllTests(IOR_test_t *tests_head); void PrintLongSummaryHeader(); diff --git a/src/ior-output.c b/src/ior-output.c index c5bffae..e6c20d3 100644 --- a/src/ior-output.c +++ b/src/ior-output.c @@ -14,6 +14,7 @@ static struct results *bw_values(int reps, IOR_offset_t *agg_file_size, double * static struct results *ops_values(int reps, IOR_offset_t *agg_file_size, IOR_offset_t transfer_size, double *vals); static double mean_of_array_of_doubles(double *values, int len); static void PPDouble(int leftjustify, double number, char *append); +static void PrintNextToken(); void PrintTableHeader(){ if (outputFormat == OUTPUT_DEFAULT){ @@ -24,8 +25,20 @@ void PrintTableHeader(){ } static int indent = 0; +static int needNextToken = 0; + +static void PrintIndent(){ + if(outputFormat == OUTPUT_CSV){ + return; + } + for(int i=0; i < indent; i++){ + fprintf(out_resultfile, " "); + } +} + static void PrintKeyValStart(char * key){ + PrintNextToken(); if (outputFormat == OUTPUT_DEFAULT){ for(int i=0; i < indent; i++){ fprintf(out_resultfile, " "); @@ -41,9 +54,13 @@ static void PrintKeyValStart(char * key){ } static void PrintNextToken(){ - if(outputFormat == OUTPUT_JSON){ - fprintf(out_resultfile, ", \n"); + if(needNextToken){ + needNextToken = 0; + if(outputFormat == OUTPUT_JSON){ + fprintf(out_resultfile, ", \n"); + } } + PrintIndent(); } static void PrintKeyValEnd(){ @@ -53,15 +70,7 @@ static void PrintKeyValEnd(){ if (outputFormat == OUTPUT_DEFAULT){ fprintf(out_resultfile, "\n"); } -} - -static void PrintIndent(){ - if(outputFormat == OUTPUT_CSV){ - return; - } - for(int i=0; i < indent; i++){ - fprintf(out_resultfile, " "); - } + needNextToken = 1; } static void PrintKeyVal(char * key, char * value){ @@ -69,7 +78,8 @@ static void PrintKeyVal(char * key, char * value){ // remove \n value[strlen(value) -1 ] = 0; } - PrintIndent(); + PrintNextToken(); + needNextToken = 1; if (outputFormat == OUTPUT_DEFAULT){ fprintf(out_resultfile, "%s: %s\n", key, value); return; @@ -81,8 +91,24 @@ static void PrintKeyVal(char * key, char * value){ } } +static void PrintKeyValDouble(char * key, double value){ + PrintNextToken(); + needNextToken = 1; + if (outputFormat == OUTPUT_DEFAULT){ + fprintf(out_resultfile, "%s: %.4f\n", key, value); + return; + } + if(outputFormat == OUTPUT_JSON){ + fprintf(out_resultfile, "\"%s\": %.4f", key, value); + }else if(outputFormat == OUTPUT_CSV){ + fprintf(out_resultfile, "%.4f", value); + } +} + + static void PrintKeyValInt(char * key, int64_t value){ - PrintIndent(); + PrintNextToken(); + needNextToken = 1; if (outputFormat == OUTPUT_DEFAULT){ fprintf(out_resultfile, "%s: %lld\n", key, (long long) value); return; @@ -95,14 +121,18 @@ static void PrintKeyValInt(char * key, int64_t value){ } static void PrintStartSection(){ - indent++; + PrintNextToken(); + needNextToken = 0; if(outputFormat == OUTPUT_JSON){ + PrintIndent(); fprintf(out_resultfile, "{\n"); } + indent++; } static void PrintNamedSectionStart(char * key){ - PrintIndent(); + PrintNextToken(); + needNextToken = 0; indent++; if(outputFormat == OUTPUT_JSON){ fprintf(out_resultfile, "\"%s\": {\n", key); @@ -111,23 +141,52 @@ static void PrintNamedSectionStart(char * key){ } } -static void PrintEndSection(){ - indent--; +static void PrintNamedArrayStart(char * key){ + PrintNextToken(); + needNextToken = 0; + indent++; if(outputFormat == OUTPUT_JSON){ - fprintf(out_resultfile, "\n}\n"); + fprintf(out_resultfile, "\"%s\": [\n", key); + }else if(outputFormat == OUTPUT_DEFAULT){ + fprintf(out_resultfile, "%s: \n", key); } } +static void PrintEndSection(){ + indent--; + if(outputFormat == OUTPUT_JSON){ + fprintf(out_resultfile, "\n"); + PrintIndent(); + fprintf(out_resultfile, "}\n"); + } + needNextToken = 1; +} + static void PrintArrayStart(char * key){ + PrintNextToken(); + needNextToken = 0; if(outputFormat == OUTPUT_JSON){ fprintf(out_resultfile, "\"%s\": [\n", key); } } static void PrintArrayEnd(){ + indent--; if(outputFormat == OUTPUT_JSON){ fprintf(out_resultfile, "]\n"); } + needNextToken = 1; +} + +void PrintRepeatEnd(){ + PrintEndSection(); +} + +void PrintRepeatStart(){ + if( outputFormat == OUTPUT_DEFAULT){ + return; + } + PrintStartSection(); } void PrintTestEnds(){ @@ -141,15 +200,26 @@ void PrintTestEnds(){ } void PrintReducedResult(IOR_test_t *test, int access, double bw, double *diff_subset, double totalTime, int rep){ - fprintf(out_resultfile, "%-10s", access == WRITE ? "write" : "read"); - PPDouble(1, bw / MEBIBYTE, " "); - PPDouble(1, (double)test->params.blockSize / KIBIBYTE, " "); - PPDouble(1, (double)test->params.transferSize / KIBIBYTE, " "); - PPDouble(1, diff_subset[0], " "); - PPDouble(1, diff_subset[1], " "); - PPDouble(1, diff_subset[2], " "); - PPDouble(1, totalTime, " "); - fprintf(out_resultfile, "%-4d\n", rep); + if (outputFormat == OUTPUT_DEFAULT){ + fprintf(out_resultfile, "%-10s", access == WRITE ? "write" : "read"); + PPDouble(1, bw / MEBIBYTE, " "); + PPDouble(1, (double)test->params.blockSize / KIBIBYTE, " "); + PPDouble(1, (double)test->params.transferSize / KIBIBYTE, " "); + PPDouble(1, diff_subset[0], " "); + PPDouble(1, diff_subset[1], " "); + PPDouble(1, diff_subset[2], " "); + PPDouble(1, totalTime, " "); + fprintf(out_resultfile, "%-4d\n", rep); + }else if (outputFormat == OUTPUT_JSON){ + PrintKeyVal("access", access == WRITE ? "write" : "read"); + PrintKeyValDouble("bwMiB", bw / MEBIBYTE); + PrintKeyValDouble("blockKiB", (double)test->params.blockSize / KIBIBYTE); + PrintKeyValDouble("xferKiB", (double)test->params.transferSize / KIBIBYTE); + PrintKeyValDouble("openTime", diff_subset[0]); + PrintKeyValDouble("wrRdTime", diff_subset[1]); + PrintKeyValDouble("closeTime", diff_subset[2]); + PrintKeyValDouble("totalTime", totalTime); + } fflush(out_resultfile); } @@ -177,14 +247,12 @@ void PrintHeader(int argc, char **argv) PrintStartSection(); PrintKeyVal("Began", CurrentTimeString()); - PrintNextToken(); PrintKeyValStart("Command line"); fprintf(out_resultfile, "%s", argv[0]); for (i = 1; i < argc; i++) { fprintf(out_resultfile, " %s", argv[i]); } PrintKeyValEnd(); - PrintNextToken(); if (uname(&unamebuf) != 0) { EWARN("uname failed"); PrintKeyVal("Machine", "Unknown"); @@ -223,7 +291,6 @@ void PrintHeader(int argc, char **argv) fprintf(out_logfile, "ENDING ENVIRON LOOP\n"); } - PrintNextToken(); PrintArrayStart("tests"); fflush(out_resultfile); fflush(out_logfile); @@ -232,17 +299,79 @@ void PrintHeader(int argc, char **argv) /* * Print header information for test output. */ -void ShowTestInfo(IOR_param_t *params) +void ShowTestStart(IOR_param_t *test) { PrintStartSection(); - PrintKeyValInt("TestID", params->id); - PrintNextToken(); + PrintKeyValInt("TestID", test->id); PrintKeyVal("StartTime", CurrentTimeString()); - PrintNextToken(); /* if pvfs2:, then skip */ - if (Regex(params->testFileName, "^[a-z][a-z].*:") == 0) { - DisplayFreespace(params); + if (Regex(test->testFileName, "^[a-z][a-z].*:") == 0) { + DisplayFreespace(test); } + + if (verbose >= VERBOSE_3 || outputFormat == OUTPUT_JSON) { + char* data_packets[] = {"g","t","o","i"}; + + PrintNamedSectionStart("Parameters"); + PrintKeyValInt("testID", test->id); + PrintKeyValInt("refnum", test->referenceNumber); + PrintKeyVal("api", test->api); + PrintKeyVal("platform", test->platform); + PrintKeyVal("testFileName", test->testFileName); + PrintKeyVal("hintsFileName", test->hintsFileName); + PrintKeyValInt("deadlineForStonewall", test->deadlineForStonewalling); + PrintKeyValInt("stoneWallingWearOut", test->stoneWallingWearOut); + PrintKeyValInt("maxTimeDuration", test->maxTimeDuration); + PrintKeyValInt("outlierThreshold", test->outlierThreshold); + PrintKeyVal("options", test->options); + PrintKeyValInt("nodes", test->nodes); + PrintKeyValInt("memoryPerTask", (unsigned long) test->memoryPerTask); + PrintKeyValInt("memoryPerNode", (unsigned long) test->memoryPerNode); + PrintKeyValInt("tasksPerNode", tasksPerNode); + PrintKeyValInt("repetitions", test->repetitions); + PrintKeyValInt("multiFile", test->multiFile); + PrintKeyValInt("interTestDelay", test->interTestDelay); + PrintKeyValInt("fsync", test->fsync); + PrintKeyValInt("fsyncperwrite", test->fsyncPerWrite); + PrintKeyValInt("useExistingTestFile", test->useExistingTestFile); + PrintKeyValInt("showHints", test->showHints); + PrintKeyValInt("uniqueDir", test->uniqueDir); + PrintKeyValInt("showHelp", test->showHelp); + PrintKeyValInt("individualDataSets", test->individualDataSets); + PrintKeyValInt("singleXferAttempt", test->singleXferAttempt); + PrintKeyValInt("readFile", test->readFile); + PrintKeyValInt("writeFile", test->writeFile); + PrintKeyValInt("filePerProc", test->filePerProc); + PrintKeyValInt("reorderTasks", test->reorderTasks); + PrintKeyValInt("reorderTasksRandom", test->reorderTasksRandom); + PrintKeyValInt("reorderTasksRandomSeed", test->reorderTasksRandomSeed); + PrintKeyValInt("randomOffset", test->randomOffset); + PrintKeyValInt("checkWrite", test->checkWrite); + PrintKeyValInt("checkRead", test->checkRead); + PrintKeyValInt("preallocate", test->preallocate); + PrintKeyValInt("useFileView", test->useFileView); + PrintKeyValInt("setAlignment", test->setAlignment); + PrintKeyValInt("storeFileOffset", test->storeFileOffset); + PrintKeyValInt("useSharedFilePointer", test->useSharedFilePointer); + PrintKeyValInt("useO_DIRECT", test->useO_DIRECT); + PrintKeyValInt("useStridedDatatype", test->useStridedDatatype); + PrintKeyValInt("keepFile", test->keepFile); + PrintKeyValInt("keepFileWithError", test->keepFileWithError); + PrintKeyValInt("quitOnError", test->quitOnError); + PrintKeyValInt("verbose", verbose); + PrintKeyVal("data packet type", data_packets[test->dataPacketType]); + PrintKeyValInt("setTimeStampSignature/incompressibleSeed", test->setTimeStampSignature); /* Seed value was copied into setTimeStampSignature as well */ + PrintKeyValInt("collective", test->collective); + PrintKeyValInt("segmentCount", test->segmentCount); + #ifdef HAVE_GPFS_FCNTL_H + PrintKeyValInt("gpfsHintAccess", test->gpfs_hint_access); + PrintKeyValInt("gpfsReleaseToken", test->gpfs_release_token); + #endif + PrintKeyValInt("transferSize", test->transferSize); + PrintKeyValInt("blockSize", test->blockSize); + PrintEndSection(); + } + fflush(out_resultfile); } @@ -266,158 +395,56 @@ void ShowSetup(IOR_param_t *params) fprintf(out_logfile, "\n*** DEBUG MODE ***\n"); fprintf(out_logfile, "*** %s ***\n\n", params->debug); } - PrintNamedSectionStart("Flags"); + PrintNamedSectionStart("Options"); PrintKeyVal("api", params->apiVersion); - PrintNextToken(); PrintKeyVal("test filename", params->testFileName); - PrintNextToken(); PrintKeyVal("access", params->filePerProc ? "file-per-process" : "single-shared-file"); - PrintNextToken(); PrintKeyVal("type", params->collective == FALSE ? "independent" : "collective"); - PrintNextToken(); - if (verbose >= VERBOSE_1) { - if (params->segmentCount > 1) { - fprintf(out_resultfile, - "\tpattern = strided (%d segments)\n", - (int)params->segmentCount); - } else { - fprintf(out_resultfile, - "\tpattern = segmented (1 segment)\n"); - } - } - fprintf(out_resultfile, "\tordering in a file ="); - if (params->randomOffset == FALSE) { - fprintf(out_resultfile, " sequential offsets\n"); - } else { - fprintf(out_resultfile, " random offsets\n"); - } - fprintf(out_resultfile, "\tordering inter file="); - if (params->reorderTasks == FALSE && params->reorderTasksRandom == FALSE) { - fprintf(out_resultfile, " no tasks offsets\n"); - } - if (params->reorderTasks == TRUE) { - fprintf(out_resultfile, " constant task offsets = %d\n", - params->taskPerNodeOffset); - } - if (params->reorderTasksRandom == TRUE) { - fprintf(out_resultfile, " random task offsets >= %d, seed=%d\n", - params->taskPerNodeOffset, params->reorderTasksRandomSeed); - } - fprintf(out_resultfile, "\tclients = %d (%d per node)\n", - params->numTasks, params->tasksPerNode); - if (params->memoryPerTask != 0) - fprintf(out_resultfile, "\tmemoryPerTask = %s\n", - HumanReadable(params->memoryPerTask, BASE_TWO)); - if (params->memoryPerNode != 0) - fprintf(out_resultfile, "\tmemoryPerNode = %s\n", - HumanReadable(params->memoryPerNode, BASE_TWO)); - fprintf(out_resultfile, "\trepetitions = %d\n", params->repetitions); - fprintf(out_resultfile, "\txfersize = %s\n", - HumanReadable(params->transferSize, BASE_TWO)); - fprintf(out_resultfile, "\tblocksize = %s\n", - HumanReadable(params->blockSize, BASE_TWO)); - fprintf(out_resultfile, "\taggregate filesize = %s\n", - HumanReadable(params->expectedAggFileSize, BASE_TWO)); + PrintKeyValInt("segments", params->segmentCount); + PrintKeyVal("ordering in a file", params->randomOffset ? "sequential" : "random"); + if (params->reorderTasks == FALSE && params->reorderTasksRandom == FALSE) { + PrintKeyVal("ordering inter file", "no tasks offsets"); + } + if (params->reorderTasks == TRUE) { + PrintKeyVal("ordering inter file", "constant task offset"); + PrintKeyValInt("task offset", params->taskPerNodeOffset); + } + if (params->reorderTasksRandom == TRUE) { + PrintKeyVal("ordering inter file", "random task offset"); + PrintKeyValInt("task offset", params->taskPerNodeOffset); + PrintKeyValInt("reorder random seed", params->reorderTasksRandomSeed); + } + PrintKeyValInt("tasks", params->numTasks); + PrintKeyValInt("clients per node", params->tasksPerNode); + if (params->memoryPerTask != 0){ + PrintKeyVal("memoryPerTask", HumanReadable(params->memoryPerTask, BASE_TWO)); + } + if (params->memoryPerNode != 0){ + PrintKeyVal("memoryPerNode", HumanReadable(params->memoryPerNode, BASE_TWO)); + } + PrintKeyValInt("repetitions", params->repetitions); + PrintKeyVal("xfersize", HumanReadable(params->transferSize, BASE_TWO)); + PrintKeyVal("blocksize", HumanReadable(params->blockSize, BASE_TWO)); + PrintKeyVal("aggregate filesize", HumanReadable(params->expectedAggFileSize, BASE_TWO)); + #ifdef HAVE_LUSTRE_LUSTRE_USER_H - if (params->lustre_set_striping) { - fprintf(out_resultfile, "\tLustre stripe size = %s\n", - ((params->lustre_stripe_size == 0) ? "Use default" : - HumanReadable(params->lustre_stripe_size, BASE_TWO))); - if (params->lustre_stripe_count == 0) { - fprintf(out_resultfile, "\t stripe count = %s\n", "Use default"); - } else { - fprintf(out_resultfile, "\t stripe count = %d\n", - params->lustre_stripe_count); - } - } + if (params->lustre_set_striping) { + PrintKeyVal("Lustre stripe size", ((params->lustre_stripe_size == 0) ? "Use default" : + HumanReadable(params->lustre_stripe_size, BASE_TWO))); + PrintKeyVal("stripe count", (params->lustre_stripe_count == 0 ? "Use default" : HumanReadable(params->lustre_stripe_count, BASE_TWO))); + } #endif /* HAVE_LUSTRE_LUSTRE_USER_H */ - if (params->deadlineForStonewalling > 0) { - fprintf(out_resultfile, "\tUsing stonewalling = %d second(s)%s\n", - params->deadlineForStonewalling, params->stoneWallingWearOut ? " with phase out" : ""); - } - PrintEndSection(); - fflush(out_resultfile); + if (params->deadlineForStonewalling > 0) { + PrintKeyValInt("stonewallingTime", params->deadlineForStonewalling); + PrintKeyValInt("stoneWallingWearOut", params->stoneWallingWearOut ); + } + PrintEndSection(); + + PrintNamedArrayStart("Results"); + + fflush(out_resultfile); } -/* - * Show test description. - */ -void ShowTest(IOR_param_t * test) -{ - const char* data_packets[] = {"g", "t","o","i"}; - - fprintf(out_resultfile, "TEST:\t%s=%d\n", "id", test->id); - fprintf(out_resultfile, "\t%s=%d\n", "refnum", test->referenceNumber); - fprintf(out_resultfile, "\t%s=%s\n", "api", test->api); - fprintf(out_resultfile, "\t%s=%s\n", "platform", test->platform); - fprintf(out_resultfile, "\t%s=%s\n", "testFileName", test->testFileName); - fprintf(out_resultfile, "\t%s=%s\n", "hintsFileName", test->hintsFileName); - fprintf(out_resultfile, "\t%s=%d\n", "deadlineForStonewall", - test->deadlineForStonewalling); - fprintf(out_resultfile, "\t%s=%d\n", "stoneWallingWearOut", test->stoneWallingWearOut); - fprintf(out_resultfile, "\t%s=%d\n", "maxTimeDuration", test->maxTimeDuration); - fprintf(out_resultfile, "\t%s=%d\n", "outlierThreshold", - test->outlierThreshold); - fprintf(out_resultfile, "\t%s=%s\n", "options", test->options); - fprintf(out_resultfile, "\t%s=%d\n", "nodes", test->nodes); - fprintf(out_resultfile, "\t%s=%lu\n", "memoryPerTask", (unsigned long) test->memoryPerTask); - fprintf(out_resultfile, "\t%s=%lu\n", "memoryPerNode", (unsigned long) test->memoryPerNode); - fprintf(out_resultfile, "\t%s=%d\n", "tasksPerNode", tasksPerNode); - fprintf(out_resultfile, "\t%s=%d\n", "repetitions", test->repetitions); - fprintf(out_resultfile, "\t%s=%d\n", "multiFile", test->multiFile); - fprintf(out_resultfile, "\t%s=%d\n", "interTestDelay", test->interTestDelay); - fprintf(out_resultfile, "\t%s=%d\n", "fsync", test->fsync); - fprintf(out_resultfile, "\t%s=%d\n", "fsYncperwrite", test->fsyncPerWrite); - fprintf(out_resultfile, "\t%s=%d\n", "useExistingTestFile", - test->useExistingTestFile); - fprintf(out_resultfile, "\t%s=%d\n", "showHints", test->showHints); - fprintf(out_resultfile, "\t%s=%d\n", "uniqueDir", test->uniqueDir); - fprintf(out_resultfile, "\t%s=%d\n", "showHelp", test->showHelp); - fprintf(out_resultfile, "\t%s=%d\n", "individualDataSets", - test->individualDataSets); - fprintf(out_resultfile, "\t%s=%d\n", "singleXferAttempt", - test->singleXferAttempt); - fprintf(out_resultfile, "\t%s=%d\n", "readFile", test->readFile); - fprintf(out_resultfile, "\t%s=%d\n", "writeFile", test->writeFile); - fprintf(out_resultfile, "\t%s=%d\n", "filePerProc", test->filePerProc); - fprintf(out_resultfile, "\t%s=%d\n", "reorderTasks", test->reorderTasks); - fprintf(out_resultfile, "\t%s=%d\n", "reorderTasksRandom", - test->reorderTasksRandom); - fprintf(out_resultfile, "\t%s=%d\n", "reorderTasksRandomSeed", - test->reorderTasksRandomSeed); - fprintf(out_resultfile, "\t%s=%d\n", "randomOffset", test->randomOffset); - fprintf(out_resultfile, "\t%s=%d\n", "checkWrite", test->checkWrite); - fprintf(out_resultfile, "\t%s=%d\n", "checkRead", test->checkRead); - fprintf(out_resultfile, "\t%s=%d\n", "preallocate", test->preallocate); - fprintf(out_resultfile, "\t%s=%d\n", "useFileView", test->useFileView); - fprintf(out_resultfile, "\t%s=%lld\n", "setAlignment", test->setAlignment); - fprintf(out_resultfile, "\t%s=%d\n", "storeFileOffset", test->storeFileOffset); - fprintf(out_resultfile, "\t%s=%d\n", "useSharedFilePointer", - test->useSharedFilePointer); - fprintf(out_resultfile, "\t%s=%d\n", "useO_DIRECT", test->useO_DIRECT); - fprintf(out_resultfile, "\t%s=%d\n", "useStridedDatatype", - test->useStridedDatatype); - fprintf(out_resultfile, "\t%s=%d\n", "keepFile", test->keepFile); - fprintf(out_resultfile, "\t%s=%d\n", "keepFileWithError", - test->keepFileWithError); - fprintf(out_resultfile, "\t%s=%d\n", "quitOnError", test->quitOnError); - fprintf(out_resultfile, "\t%s=%d\n", "verbose", verbose); - fprintf(out_resultfile, "\t%s=%s\n", "data packet type", data_packets[test->dataPacketType]); - fprintf(out_resultfile, "\t%s=%d\n", "setTimeStampSignature/incompressibleSeed", - test->setTimeStampSignature); /* Seed value was copied into setTimeStampSignature as well */ - fprintf(out_resultfile, "\t%s=%d\n", "collective", test->collective); - fprintf(out_resultfile, "\t%s=%lld", "segmentCount", test->segmentCount); -#ifdef HAVE_GPFS_FCNTL_H - fprintf(out_resultfile, "\t%s=%d\n", "gpfsHintAccess", test->gpfs_hint_access); - fprintf(out_resultfile, "\t%s=%d\n", "gpfsReleaseToken", test->gpfs_release_token); -#endif - if (strcasecmp(test->api, "HDF5") == 0) { - fprintf(out_resultfile, " (datasets)"); - } - fprintf(out_resultfile, "\n"); - fprintf(out_resultfile, "\t%s=%lld\n", "transferSize", test->transferSize); - fprintf(out_resultfile, "\t%s=%lld\n", "blockSize", test->blockSize); -} /* @@ -441,32 +468,68 @@ void PrintLongSummaryOneOperation(IOR_test_t *test, double *times, char *operati ops = ops_values(reps, results->aggFileSizeForBW, params->transferSize, times); - fprintf(out_resultfile, "%-9s ", operation); - fprintf(out_resultfile, "%10.2f ", bw->max / MEBIBYTE); - fprintf(out_resultfile, "%10.2f ", bw->min / MEBIBYTE); - fprintf(out_resultfile, "%10.2f ", bw->mean / MEBIBYTE); - fprintf(out_resultfile, "%10.2f ", bw->sd / MEBIBYTE); - fprintf(out_resultfile, "%10.2f ", ops->max); - fprintf(out_resultfile, "%10.2f ", ops->min); - fprintf(out_resultfile, "%10.2f ", ops->mean); - fprintf(out_resultfile, "%10.2f ", ops->sd); - fprintf(out_resultfile, "%10.5f ", mean_of_array_of_doubles(times, reps)); - fprintf(out_resultfile, "%5d ", params->id); - fprintf(out_resultfile, "%6d ", params->numTasks); - fprintf(out_resultfile, "%3d ", params->tasksPerNode); - fprintf(out_resultfile, "%4d ", params->repetitions); - fprintf(out_resultfile, "%3d ", params->filePerProc); - fprintf(out_resultfile, "%5d ", params->reorderTasks); - fprintf(out_resultfile, "%8d ", params->taskPerNodeOffset); - fprintf(out_resultfile, "%9d ", params->reorderTasksRandom); - fprintf(out_resultfile, "%4d ", params->reorderTasksRandomSeed); - fprintf(out_resultfile, "%6lld ", params->segmentCount); - fprintf(out_resultfile, "%8lld ", params->blockSize); - fprintf(out_resultfile, "%8lld ", params->transferSize); - fprintf(out_resultfile, "%9.1f ", (float)results->aggFileSizeForBW[0] / MEBIBYTE); - fprintf(out_resultfile, "%3s ", params->api); - fprintf(out_resultfile, "%6d", params->referenceNumber); - fprintf(out_resultfile, "\n"); + if(outputFormat == OUTPUT_DEFAULT){ + fprintf(out_resultfile, "%-9s ", operation); + fprintf(out_resultfile, "%10.2f ", bw->max / MEBIBYTE); + fprintf(out_resultfile, "%10.2f ", bw->min / MEBIBYTE); + fprintf(out_resultfile, "%10.2f ", bw->mean / MEBIBYTE); + fprintf(out_resultfile, "%10.2f ", bw->sd / MEBIBYTE); + fprintf(out_resultfile, "%10.2f ", ops->max); + fprintf(out_resultfile, "%10.2f ", ops->min); + fprintf(out_resultfile, "%10.2f ", ops->mean); + fprintf(out_resultfile, "%10.2f ", ops->sd); + fprintf(out_resultfile, "%10.5f ", mean_of_array_of_doubles(times, reps)); + fprintf(out_resultfile, "%5d ", params->id); + fprintf(out_resultfile, "%6d ", params->numTasks); + fprintf(out_resultfile, "%3d ", params->tasksPerNode); + fprintf(out_resultfile, "%4d ", params->repetitions); + fprintf(out_resultfile, "%3d ", params->filePerProc); + fprintf(out_resultfile, "%5d ", params->reorderTasks); + fprintf(out_resultfile, "%8d ", params->taskPerNodeOffset); + fprintf(out_resultfile, "%9d ", params->reorderTasksRandom); + fprintf(out_resultfile, "%4d ", params->reorderTasksRandomSeed); + fprintf(out_resultfile, "%6lld ", params->segmentCount); + fprintf(out_resultfile, "%8lld ", params->blockSize); + fprintf(out_resultfile, "%8lld ", params->transferSize); + fprintf(out_resultfile, "%9.1f ", (float)results->aggFileSizeForBW[0] / MEBIBYTE); + fprintf(out_resultfile, "%3s ", params->api); + fprintf(out_resultfile, "%6d", params->referenceNumber); + fprintf(out_resultfile, "\n"); + }else if (outputFormat == OUTPUT_JSON){ + PrintStartSection(); + PrintKeyVal("operation", operation); + PrintKeyVal("API", params->api); + PrintKeyValInt("TestID", params->id); + PrintKeyValInt("ReferenceNumber", params->referenceNumber); + PrintKeyValInt("segmentCount", params->segmentCount); + PrintKeyValInt("blockSize", params->blockSize); + PrintKeyValInt("transferSize", params->transferSize); + PrintKeyValInt("numTasks", params->numTasks); + PrintKeyValInt("tasksPerNode", params->tasksPerNode); + PrintKeyValInt("repetitions", params->repetitions); + PrintKeyValInt("filePerProc", params->filePerProc); + PrintKeyValInt("reorderTasks", params->reorderTasks); + PrintKeyValInt("taskPerNodeOffset", params->taskPerNodeOffset); + PrintKeyValInt("reorderTasksRandom", params->reorderTasksRandom); + PrintKeyValInt("reorderTasksRandomSeed", params->reorderTasksRandomSeed); + PrintKeyValInt("segmentCount", params->segmentCount); + PrintKeyValInt("blockSize", params->blockSize); + PrintKeyValInt("transferSize", params->transferSize); + PrintKeyValDouble("bwMaxMIB", bw->max / MEBIBYTE); + PrintKeyValDouble("bwMinMIB", bw->min / MEBIBYTE); + PrintKeyValDouble("bwMeanMIB", bw->mean / MEBIBYTE); + PrintKeyValDouble("bwStdMIB", bw->sd / MEBIBYTE); + PrintKeyValDouble("OPsMax", ops->max); + PrintKeyValDouble("OPsMin", ops->min); + PrintKeyValDouble("OPsMean", ops->mean); + PrintKeyValDouble("OPsSD", ops->sd); + PrintKeyValDouble("MeanTime", mean_of_array_of_doubles(times, reps)); + PrintKeyValDouble("xsizeMiB", (double) results->aggFileSizeForBW[0] / MEBIBYTE); + PrintEndSection(); + }else if (outputFormat == OUTPUT_CSV){ + + } + fflush(out_resultfile); free(bw); @@ -488,6 +551,9 @@ void PrintLongSummaryHeader() { if (rank != 0 || verbose < VERBOSE_0) return; + if(outputFormat != OUTPUT_DEFAULT){ + return; + } fprintf(out_resultfile, "\n"); fprintf(out_resultfile, "%-9s %10s %10s %10s %10s %10s %10s %10s %10s %10s", @@ -502,17 +568,28 @@ void PrintLongSummaryHeader() void PrintLongSummaryAllTests(IOR_test_t *tests_head) { - IOR_test_t *tptr; - if (rank != 0 || verbose < VERBOSE_0) - return; + IOR_test_t *tptr; + if (rank != 0 || verbose < VERBOSE_0) + return; - fprintf(out_resultfile, "\n"); - fprintf(out_resultfile, "Summary of all tests:"); - PrintLongSummaryHeader(); + PrintArrayEnd(); - for (tptr = tests_head; tptr != NULL; tptr = tptr->next) { - PrintLongSummaryOneTest(tptr); - } + if(outputFormat == OUTPUT_DEFAULT){ + fprintf(out_resultfile, "\n"); + fprintf(out_resultfile, "Summary of all tests:"); + }else if (outputFormat == OUTPUT_JSON){ + PrintNamedArrayStart("summary"); + }else if (outputFormat == OUTPUT_CSV){ + + } + + PrintLongSummaryHeader(); + + for (tptr = tests_head; tptr != NULL; tptr = tptr->next) { + PrintLongSummaryOneTest(tptr); + } + + PrintArrayEnd(); } void PrintShortSummary(IOR_test_t * test) @@ -528,6 +605,8 @@ void PrintShortSummary(IOR_test_t * test) if (rank != 0 || verbose < VERBOSE_0) return; + PrintArrayEnd(); + reps = params->repetitions; max_write = results->writeTime[0]; @@ -539,14 +618,26 @@ void PrintShortSummary(IOR_test_t * test) max_read = MAX(bw, max_read); } - fprintf(out_resultfile, "\n"); - if (params->writeFile) { - fprintf(out_resultfile, "Max Write: %.2f MiB/sec (%.2f MB/sec)\n", - max_write/MEBIBYTE, max_write/MEGABYTE); - } - if (params->readFile) { - fprintf(out_resultfile, "Max Read: %.2f MiB/sec (%.2f MB/sec)\n", - max_read/MEBIBYTE, max_read/MEGABYTE); + if(outputFormat == OUTPUT_DEFAULT){ + if (params->writeFile) { + fprintf(out_resultfile, "Max Write: %.2f MiB/sec (%.2f MB/sec)\n", + max_write/MEBIBYTE, max_write/MEGABYTE); + } + if (params->readFile) { + fprintf(out_resultfile, "Max Read: %.2f MiB/sec (%.2f MB/sec)\n", + max_read/MEBIBYTE, max_read/MEGABYTE); + } + }else if (outputFormat == OUTPUT_JSON){ + PrintNamedSectionStart("max"); + if (params->writeFile) { + PrintKeyValDouble("writeMiB", max_write/MEBIBYTE); + PrintKeyValDouble("writeMB", max_write/MEGABYTE); + } + if (params->readFile) { + PrintKeyValDouble("readMiB", max_read/MEBIBYTE); + PrintKeyValDouble("readMB", max_read/MEGABYTE); + } + PrintEndSection(); } } @@ -587,9 +678,13 @@ void PrintRemoveTiming(double start, double finish, int rep) if (rank != 0 || verbose < VERBOSE_0) return; - fprintf(out_resultfile, "remove - - - - - - "); - PPDouble(1, finish-start, " "); - fprintf(out_resultfile, "%-4d\n", rep); + if (outputFormat == OUTPUT_DEFAULT){ + fprintf(out_resultfile, "remove - - - - - - "); + PPDouble(1, finish-start, " "); + fprintf(out_resultfile, "%-4d\n", rep); + }else if (outputFormat == OUTPUT_JSON){ + PrintKeyValDouble("removeTime", finish - start); + } } diff --git a/src/ior.c b/src/ior.c index 3ece7e6..c09aeed 100755 --- a/src/ior.c +++ b/src/ior.c @@ -87,12 +87,8 @@ IOR_test_t * ior_run(int argc, char **argv, MPI_Comm world_com, FILE * world_out verbose = tptr->params.verbose; tptr->params.testComm = world_com; if (rank == 0 && verbose >= VERBOSE_0) { - ShowTestInfo(&tptr->params); + ShowTestStart(&tptr->params); } - if (rank == 0 && verbose >= VERBOSE_3) { - ShowTest(&tptr->params); - } - TestIoSys(tptr); tptr->results->errors = totalErrorCount; ShowTestEnd(tptr); @@ -175,10 +171,7 @@ int ior_main(int argc, char **argv) for (tptr = tests_head; tptr != NULL; tptr = tptr->next) { verbose = tptr->params.verbose; if (rank == 0 && verbose >= VERBOSE_0) { - ShowTestInfo(&tptr->params); - } - if (rank == 0 && verbose >= VERBOSE_3) { - ShowTest(&tptr->params); + ShowTestStart(&tptr->params); } // This is useful for trapping a running MPI process. While @@ -1371,7 +1364,7 @@ static void TestIoSys(IOR_test_t *test) /* loop over test iterations */ for (rep = 0; rep < params->repetitions; rep++) { - + PrintRepeatStart(); /* Get iteration start time in seconds in task 0 and broadcast to all tasks */ if (rank == 0) { @@ -1599,6 +1592,8 @@ static void TestIoSys(IOR_test_t *test) } params->errorFound = FALSE; rankOffset = 0; + + PrintRepeatEnd(); } MPI_CHECK(MPI_Comm_free(&testComm), "MPI_Comm_free() error"); diff --git a/src/utilities.c b/src/utilities.c index dd20177..40c9c33 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -417,11 +417,11 @@ void ShowFileSystemSize(char *fileSystem) usedInodePercentage); fflush(out_logfile); }else if(outputFormat == OUTPUT_JSON){ - fprintf(out_resultfile, " \"Path\": \"%s\",", realPath); + fprintf(out_resultfile, " , \"Path\": \"%s\",", realPath); fprintf(out_resultfile, "\"Capacity\": \"%.1f %s\", \"Used Capacity\": \"%2.1f%%\",", totalFileSystemSizeHR, fileSystemUnitStr, usedFileSystemPercentage); - fprintf(out_resultfile, "\"Inodes\": \"%.1f Mi\", \"Used Inodes\" : \"%2.1f%%\",\n", + fprintf(out_resultfile, "\"Inodes\": \"%.1f Mi\", \"Used Inodes\" : \"%2.1f%%\"\n", (double)totalInodes / (double)(1<<20), usedInodePercentage); }else if(outputFormat == OUTPUT_CSV){ From 0f17d4860f76f7ae5b64b2eb1f0c3155e9d18697 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sun, 8 Jul 2018 16:56:04 +0100 Subject: [PATCH 45/80] Pretty printing for normal output. --- src/ior-output.c | 26 ++++++++++++++++---------- src/utilities.c | 6 +++--- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/ior-output.c b/src/ior-output.c index e6c20d3..d697c51 100644 --- a/src/ior-output.c +++ b/src/ior-output.c @@ -28,7 +28,7 @@ static int indent = 0; static int needNextToken = 0; static void PrintIndent(){ - if(outputFormat == OUTPUT_CSV){ + if(outputFormat != OUTPUT_JSON){ return; } for(int i=0; i < indent; i++){ @@ -40,10 +40,8 @@ static void PrintIndent(){ static void PrintKeyValStart(char * key){ PrintNextToken(); if (outputFormat == OUTPUT_DEFAULT){ - for(int i=0; i < indent; i++){ - fprintf(out_resultfile, " "); - } - fprintf(out_resultfile, "%s: ", key); + PrintIndent(); + fprintf(out_resultfile, "%-20s: ", key); return; } if(outputFormat == OUTPUT_JSON){ @@ -53,6 +51,12 @@ static void PrintKeyValStart(char * key){ } } +static void PrintNewLine(){ + if (outputFormat == OUTPUT_DEFAULT){ + fprintf(out_resultfile, "\n"); + } +} + static void PrintNextToken(){ if(needNextToken){ needNextToken = 0; @@ -81,7 +85,7 @@ static void PrintKeyVal(char * key, char * value){ PrintNextToken(); needNextToken = 1; if (outputFormat == OUTPUT_DEFAULT){ - fprintf(out_resultfile, "%s: %s\n", key, value); + fprintf(out_resultfile, "%-20s: %s\n", key, value); return; } if(outputFormat == OUTPUT_JSON){ @@ -95,7 +99,7 @@ static void PrintKeyValDouble(char * key, double value){ PrintNextToken(); needNextToken = 1; if (outputFormat == OUTPUT_DEFAULT){ - fprintf(out_resultfile, "%s: %.4f\n", key, value); + fprintf(out_resultfile, "%-20s: %.4f\n", key, value); return; } if(outputFormat == OUTPUT_JSON){ @@ -110,7 +114,7 @@ static void PrintKeyValInt(char * key, int64_t value){ PrintNextToken(); needNextToken = 1; if (outputFormat == OUTPUT_DEFAULT){ - fprintf(out_resultfile, "%s: %lld\n", key, (long long) value); + fprintf(out_resultfile, "%-20s: %lld\n", key, (long long) value); return; } if(outputFormat == OUTPUT_JSON){ @@ -134,10 +138,11 @@ static void PrintNamedSectionStart(char * key){ PrintNextToken(); needNextToken = 0; indent++; + if(outputFormat == OUTPUT_JSON){ fprintf(out_resultfile, "\"%s\": {\n", key); }else if(outputFormat == OUTPUT_DEFAULT){ - fprintf(out_resultfile, "%s: \n", key); + fprintf(out_resultfile, "\n%s: \n", key); } } @@ -148,7 +153,7 @@ static void PrintNamedArrayStart(char * key){ if(outputFormat == OUTPUT_JSON){ fprintf(out_resultfile, "\"%s\": [\n", key); }else if(outputFormat == OUTPUT_DEFAULT){ - fprintf(out_resultfile, "%s: \n", key); + fprintf(out_resultfile, "\n%s: \n", key); } } @@ -323,6 +328,7 @@ void ShowTestStart(IOR_param_t *test) PrintKeyValInt("stoneWallingWearOut", test->stoneWallingWearOut); PrintKeyValInt("maxTimeDuration", test->maxTimeDuration); PrintKeyValInt("outlierThreshold", test->outlierThreshold); + PrintKeyVal("options", test->options); PrintKeyValInt("nodes", test->nodes); PrintKeyValInt("memoryPerTask", (unsigned long) test->memoryPerTask); diff --git a/src/utilities.c b/src/utilities.c index 40c9c33..acc6a85 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -408,9 +408,9 @@ void ShowFileSystemSize(char *fileSystem) } if(outputFormat == OUTPUT_DEFAULT){ - fprintf(out_resultfile, "Path: %s\n", realPath); - fprintf(out_resultfile, "FS: %.1f %s Used FS: %2.1f%% ", - totalFileSystemSizeHR, fileSystemUnitStr, + fprintf(out_resultfile, "%-20s: %s\n", "Path", realPath); + fprintf(out_resultfile, "%-20s: %.1f %s Used FS: %2.1f%% ", + "FS", totalFileSystemSizeHR, fileSystemUnitStr, usedFileSystemPercentage); fprintf(out_resultfile, "Inodes: %.1f Mi Used Inodes: %2.1f%%\n", (double)totalInodes / (double)(1<<20), From 831a5ef823a5f9826057c564fa1e1269e5e37d74 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Mon, 9 Jul 2018 16:25:35 +0100 Subject: [PATCH 46/80] JSON: Slight output adaption. --- src/ior-output.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/ior-output.c b/src/ior-output.c index d697c51..8a88876 100644 --- a/src/ior-output.c +++ b/src/ior-output.c @@ -167,7 +167,15 @@ static void PrintEndSection(){ needNextToken = 1; } -static void PrintArrayStart(char * key){ +static void PrintArrayStart(){ + PrintNextToken(); + needNextToken = 0; + if(outputFormat == OUTPUT_JSON){ + fprintf(out_resultfile, "[ "); + } +} + +static void PrintArrayNamedStart(char * key){ PrintNextToken(); needNextToken = 0; if(outputFormat == OUTPUT_JSON){ @@ -184,14 +192,14 @@ static void PrintArrayEnd(){ } void PrintRepeatEnd(){ - PrintEndSection(); + PrintArrayEnd(); } void PrintRepeatStart(){ if( outputFormat == OUTPUT_DEFAULT){ return; } - PrintStartSection(); + PrintArrayStart(); } void PrintTestEnds(){ @@ -216,6 +224,7 @@ void PrintReducedResult(IOR_test_t *test, int access, double bw, double *diff_su PPDouble(1, totalTime, " "); fprintf(out_resultfile, "%-4d\n", rep); }else if (outputFormat == OUTPUT_JSON){ + PrintStartSection(); PrintKeyVal("access", access == WRITE ? "write" : "read"); PrintKeyValDouble("bwMiB", bw / MEBIBYTE); PrintKeyValDouble("blockKiB", (double)test->params.blockSize / KIBIBYTE); @@ -224,6 +233,7 @@ void PrintReducedResult(IOR_test_t *test, int access, double bw, double *diff_su PrintKeyValDouble("wrRdTime", diff_subset[1]); PrintKeyValDouble("closeTime", diff_subset[2]); PrintKeyValDouble("totalTime", totalTime); + PrintEndSection(); } fflush(out_resultfile); } @@ -296,7 +306,7 @@ void PrintHeader(int argc, char **argv) fprintf(out_logfile, "ENDING ENVIRON LOOP\n"); } - PrintArrayStart("tests"); + PrintArrayNamedStart("tests"); fflush(out_resultfile); fflush(out_logfile); } @@ -689,7 +699,10 @@ void PrintRemoveTiming(double start, double finish, int rep) PPDouble(1, finish-start, " "); fprintf(out_resultfile, "%-4d\n", rep); }else if (outputFormat == OUTPUT_JSON){ - PrintKeyValDouble("removeTime", finish - start); + PrintStartSection(); + PrintKeyVal("access", "remove"); + PrintKeyValDouble("totalTime", finish - start); + PrintEndSection(); } } From 7981691a63b3ac348c6222538076e653963abfa9 Mon Sep 17 00:00:00 2001 From: Shane Snyder Date: Fri, 23 Feb 2018 10:38:24 -0600 Subject: [PATCH 47/80] add a new AIORI backend for RADOS inform aiori interface about RADOS backend stubbed out aiori backend for rados additions to get RADOS backend compiling/linking first cut at rados create/open patha make sure to return RADOS oid on open/create implement rados xfer path for WRITE refactor + implement getfilesize and close remember to use read_op interface for stat implement RADOS delete function don't error in RADOS_Delete for now implement RADOS set_version handle open/create flags appropriately cleanup RADOS error handling implement read/readcheck/writecheck for RADOS rados doesn't support directio implement unsupported aiori ops for RADOS implement RADOS access call define rados types if no rados support --- configure.ac | 10 ++ src/Makefile.am | 4 + src/aiori-RADOS.c | 347 ++++++++++++++++++++++++++++++++++++++++++++++ src/aiori.c | 3 + src/aiori.h | 1 + src/ior.h | 11 +- 6 files changed, 375 insertions(+), 1 deletion(-) create mode 100755 src/aiori-RADOS.c diff --git a/configure.ac b/configure.ac index 3513c12..730d23b 100755 --- a/configure.ac +++ b/configure.ac @@ -131,6 +131,16 @@ AM_COND_IF([USE_POSIX_AIORI],[ AC_DEFINE([USE_POSIX_AIORI], [], [Build POSIX backend AIORI]) ]) +# RADOS support +AC_ARG_WITH([rados], + [AS_HELP_STRING([--with-rados], + [support IO with librados backend @<:@default=no@:>@])], + [], + [with_rados=no]) +AM_CONDITIONAL([USE_RADOS_AIORI], [test x$with_rados = xyes]) +AM_COND_IF([USE_RADOS_AIORI],[ + AC_DEFINE([USE_RADOS_AIORI], [], [Build RADOS backend AIORI]) +]) diff --git a/src/Makefile.am b/src/Makefile.am index 36bbde7..92011e3 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -49,6 +49,10 @@ if USE_POSIX_AIORI extraSOURCES += aiori-POSIX.c endif +if USE_RADOS_AIORI +extraSOURCES += aiori-RADOS.c +extraLDADD += -lrados +endif if USE_S3_AIORI extraSOURCES += aiori-S3.c diff --git a/src/aiori-RADOS.c b/src/aiori-RADOS.c new file mode 100755 index 0000000..6d88a7c --- /dev/null +++ b/src/aiori-RADOS.c @@ -0,0 +1,347 @@ +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + */ +/******************************************************************************\ +* * +* (C) 2015 The University of Chicago * +* * +* See COPYRIGHT in top-level directory. * +* * +******************************************************************************** +* +* Implement abstract I/O interface for RADOS. +* +\******************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include "ior.h" +#include "iordef.h" +#include "aiori.h" +#include "utilities.h" + +/**************************** P R O T O T Y P E S *****************************/ +static void *RADOS_Create(char *, IOR_param_t *); +static void *RADOS_Open(char *, IOR_param_t *); +static IOR_offset_t RADOS_Xfer(int, void *, IOR_size_t *, + IOR_offset_t, IOR_param_t *); +static void RADOS_Close(void *, IOR_param_t *); +static void RADOS_Delete(char *, IOR_param_t *); +static void RADOS_SetVersion(IOR_param_t *); +static void RADOS_Fsync(void *, IOR_param_t *); +static IOR_offset_t RADOS_GetFileSize(IOR_param_t *, MPI_Comm, char *); +static int RADOS_StatFS(const char *, ior_aiori_statfs_t *, IOR_param_t *); +static int RADOS_MkDir(const char *, mode_t, IOR_param_t *); +static int RADOS_RmDir(const char *, IOR_param_t *); +static int RADOS_Access(const char *, int, IOR_param_t *); +static int RADOS_Stat(const char *, struct stat *, IOR_param_t *); + +/************************** D E C L A R A T I O N S ***************************/ + +ior_aiori_t rados_aiori = { + .name = "RADOS", + .create = RADOS_Create, + .open = RADOS_Open, + .xfer = RADOS_Xfer, + .close = RADOS_Close, + .delete = RADOS_Delete, + .set_version = RADOS_SetVersion, + .fsync = RADOS_Fsync, + .get_file_size = RADOS_GetFileSize, + .statfs = RADOS_StatFS, + .mkdir = RADOS_MkDir, + .rmdir = RADOS_RmDir, + .access = RADOS_Access, + .stat = RADOS_Stat, +}; + +#define RADOS_ERR(__err_str, __ret) do { \ + errno = -__ret; \ + ERR(__err_str); \ +} while(0) + +/***************************** F U N C T I O N S ******************************/ + +static void RADOS_Cluster_Init(IOR_param_t * param) +{ + int ret; + + /* create RADOS cluster handle */ + /* XXX: HARDCODED RADOS USER NAME */ + ret = rados_create(¶m->rados_cluster, "admin"); + if (ret) + RADOS_ERR("unable to create RADOS cluster handle", ret); + + /* set the handle using the Ceph config */ + /* XXX: HARDCODED RADOS CONF PATH */ + ret = rados_conf_read_file(param->rados_cluster, "/etc/ceph/ceph.conf"); + if (ret) + RADOS_ERR("unable to read RADOS config file", ret); + + /* connect to the RADOS cluster */ + ret = rados_connect(param->rados_cluster); + if (ret) + RADOS_ERR("unable to connect to the RADOS cluster", ret); + + /* create an io context for the pool we are operating on */ + /* XXX: HARDCODED RADOS POOL NAME */ + ret = rados_ioctx_create(param->rados_cluster, "cephfs_data", + ¶m->rados_ioctx); + if (ret) + RADOS_ERR("unable to create an I/O context for the RADOS cluster", ret); + + return; +} + +static void RADOS_Cluster_Finalize(IOR_param_t * param) +{ + /* ioctx destroy */ + rados_ioctx_destroy(param->rados_ioctx); + + /* shutdown */ + rados_shutdown(param->rados_cluster); +} + +static void *RADOS_Create_Or_Open(char *testFileName, IOR_param_t * param, int create_flag) +{ + int ret; + char *oid; + + RADOS_Cluster_Init(param); + + if (param->useO_DIRECT == TRUE) + WARN("direct I/O mode is not implemented in RADOS\n"); + + oid = strdup(testFileName); + if (!oid) + ERR("unable to allocate RADOS oid"); + + if (create_flag) + { + rados_write_op_t create_op; + int rados_create_flag; + + if (param->openFlags & IOR_EXCL) + rados_create_flag = LIBRADOS_CREATE_EXCLUSIVE; + else + rados_create_flag = LIBRADOS_CREATE_IDEMPOTENT; + + /* create a RADOS "write op" for creating the object */ + create_op = rados_create_write_op(); + rados_write_op_create(create_op, rados_create_flag, NULL); + ret = rados_write_op_operate(create_op, param->rados_ioctx, oid, + NULL, 0); + rados_release_write_op(create_op); + if (ret) + RADOS_ERR("unable to create RADOS object", ret); + } + else + { + /* XXX actually, we should probably assert oid existence here? */ + } + + return (void *)oid; +} + +static void *RADOS_Create(char *testFileName, IOR_param_t * param) +{ + return RADOS_Create_Or_Open(testFileName, param, TRUE); +} + +static void *RADOS_Open(char *testFileName, IOR_param_t * param) +{ + if (param->openFlags & IOR_CREAT) + return RADOS_Create_Or_Open(testFileName, param, TRUE); + else + return RADOS_Create_Or_Open(testFileName, param, FALSE); +} + +static IOR_offset_t RADOS_Xfer(int access, void *fd, IOR_size_t * buffer, + IOR_offset_t length, IOR_param_t * param) +{ + int ret; + char *oid = (char *)fd; + + if (access == WRITE) + { + rados_write_op_t write_op; + + write_op = rados_create_write_op(); + rados_write_op_write(write_op, (const char *)buffer, + length, param->offset); + ret = rados_write_op_operate(write_op, param->rados_ioctx, + oid, NULL, 0); + rados_release_write_op(write_op); + if (ret) + RADOS_ERR("unable to write RADOS object", ret); + } + else /* READ */ + { + int read_ret; + size_t bytes_read; + rados_read_op_t read_op; + + read_op = rados_create_read_op(); + rados_read_op_read(read_op, param->offset, length, (char *)buffer, + &bytes_read, &read_ret); + ret = rados_read_op_operate(read_op, param->rados_ioctx, oid, 0); + rados_release_read_op(read_op); + if (ret || read_ret || ((IOR_offset_t)bytes_read != length)) + RADOS_ERR("unable to read RADOS object", ret); + } + + return length; +} + +static void RADOS_Fsync(void *fd, IOR_param_t * param) +{ + return; +} + +static void RADOS_Close(void *fd, IOR_param_t * param) +{ + char *oid = (char *)fd; + + /* object does not need to be "closed", but we should tear the cluster down */ + RADOS_Cluster_Finalize(param); + free(oid); + + return; +} + +static void RADOS_Delete(char *testFileName, IOR_param_t * param) +{ + int ret; + char *oid = testFileName; + rados_write_op_t remove_op; + + /* we have to reestablish cluster connection here... */ + RADOS_Cluster_Init(param); + + /* remove the object */ + remove_op = rados_create_write_op(); + rados_write_op_remove(remove_op); + ret = rados_write_op_operate(remove_op, param->rados_ioctx, + oid, NULL, 0); + rados_release_write_op(remove_op); + if (ret) + RADOS_ERR("unable to remove RADOS object", ret); + + RADOS_Cluster_Finalize(param); + + return; +} + +static void RADOS_SetVersion(IOR_param_t * test) +{ + strcpy(test->apiVersion, test->api); + return; +} + +static IOR_offset_t RADOS_GetFileSize(IOR_param_t * test, MPI_Comm testComm, + char *testFileName) +{ + int ret; + char *oid = testFileName; + rados_read_op_t stat_op; + uint64_t oid_size; + int stat_ret; + IOR_offset_t aggSizeFromStat, tmpMin, tmpMax, tmpSum; + + /* we have to reestablish cluster connection here... */ + RADOS_Cluster_Init(test); + + /* stat the object */ + stat_op = rados_create_read_op(); + rados_read_op_stat(stat_op, &oid_size, NULL, &stat_ret); + ret = rados_read_op_operate(stat_op, test->rados_ioctx, oid, 0); + rados_release_read_op(stat_op); + if (ret || stat_ret) + RADOS_ERR("unable to stat RADOS object", stat_ret); + aggSizeFromStat = oid_size; + + if (test->filePerProc == TRUE) + { + MPI_CHECK(MPI_Allreduce(&aggSizeFromStat, &tmpSum, 1, + MPI_LONG_LONG_INT, MPI_SUM, testComm), + "cannot total data moved"); + aggSizeFromStat = tmpSum; + } + else + { + MPI_CHECK(MPI_Allreduce(&aggSizeFromStat, &tmpMin, 1, + MPI_LONG_LONG_INT, MPI_MIN, testComm), + "cannot total data moved"); + MPI_CHECK(MPI_Allreduce(&aggSizeFromStat, &tmpMax, 1, + MPI_LONG_LONG_INT, MPI_MAX, testComm), + "cannot total data moved"); + if (tmpMin != tmpMax) + { + if (rank == 0) + WARN("inconsistent file size by different tasks"); + + /* incorrect, but now consistent across tasks */ + aggSizeFromStat = tmpMin; + } + } + + RADOS_Cluster_Finalize(test); + + return aggSizeFromStat; +} + +static int RADOS_StatFS(const char *oid, ior_aiori_statfs_t *stat_buf, + IOR_param_t *param) +{ + WARN("statfs not supported in RADOS backend!"); + return -1; +} + +static int RADOS_MkDir(const char *oid, mode_t mode, IOR_param_t *param) +{ + WARN("mkdir not supported in RADOS backend!"); + return -1; +} + +static int RADOS_RmDir(const char *oid, IOR_param_t *param) +{ + WARN("rmdir not supported in RADOS backend!"); + return -1; +} + +static int RADOS_Access(const char *oid, int mode, IOR_param_t *param) +{ + rados_read_op_t read_op; + int ret; + int prval; + uint64_t oid_size; + + /* we have to reestablish cluster connection here... */ + RADOS_Cluster_Init(param); + + /* use read_op stat to check for oid existence */ + read_op = rados_create_read_op(); + rados_read_op_stat(read_op, &oid_size, NULL, &prval); + ret = rados_read_op_operate(read_op, param->rados_ioctx, oid, 0); + rados_release_read_op(read_op); + + RADOS_Cluster_Finalize(param); + + if (ret | prval) + return -1; + else + return 0; +} + +static int RADOS_Stat(const char *oid, struct stat *buf, IOR_param_t *param) +{ + WARN("stat not supported in RADOS backend!"); + return -1; +} diff --git a/src/aiori.c b/src/aiori.c index ccabcb8..50d5951 100644 --- a/src/aiori.c +++ b/src/aiori.c @@ -48,6 +48,9 @@ ior_aiori_t *available_aiori[] = { &s3_aiori, &s3_plus_aiori, &s3_emc_aiori, +#endif +#ifdef USE_RADOS_AIORI + &rados_aiori, #endif NULL }; diff --git a/src/aiori.h b/src/aiori.h index a54bd59..9f9a0d2 100755 --- a/src/aiori.h +++ b/src/aiori.h @@ -87,6 +87,7 @@ extern ior_aiori_t posix_aiori; extern ior_aiori_t s3_aiori; extern ior_aiori_t s3_plus_aiori; extern ior_aiori_t s3_emc_aiori; +extern ior_aiori_t rados_aiori; const ior_aiori_t *aiori_select (const char *api); int aiori_count (void); diff --git a/src/ior.h b/src/ior.h index fce447b..3d6314d 100755 --- a/src/ior.h +++ b/src/ior.h @@ -37,7 +37,12 @@ typedef void IOBuf; /* unused, but needs a type */ #endif - +#ifdef USE_RADOS_AIORI +# include +#else + typedef void *rados_t; + typedef void *rados_ioctx_t; +#endif #include "iordef.h" @@ -193,6 +198,10 @@ typedef struct # define MAX_UPLOAD_ID_SIZE 256 /* seems to be 32, actually */ char UploadId[MAX_UPLOAD_ID_SIZE +1]; /* key for multi-part-uploads */ + /* RADOS variables */ + rados_t rados_cluster; /* RADOS cluster handle */ + rados_ioctx_t rados_ioctx; /* I/O context for our pool in the RADOS cluster */ + /* NCMPI variables */ int var_id; /* variable id handle for data set */ From 205b20f0f408f9e3a82814a4d285b012fd129c25 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Thu, 12 Jul 2018 16:13:40 +0100 Subject: [PATCH 48/80] Bugfix merge --- src/aiori-MMAP.c | 2 +- src/aiori-MPIIO.c | 4 ++-- src/aiori-POSIX.c | 10 +--------- src/aiori.c | 7 +++++++ src/aiori.h | 7 +++++++ src/ior.c | 2 +- testing/basic-tests.sh | 2 +- 7 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/aiori-MMAP.c b/src/aiori-MMAP.c index 7f34d51..ac622ff 100644 --- a/src/aiori-MMAP.c +++ b/src/aiori-MMAP.c @@ -42,7 +42,7 @@ ior_aiori_t mmap_aiori = { .xfer = MMAP_Xfer, .close = MMAP_Close, .delete = POSIX_Delete, - .set_version = POSIX_SetVersion, + .set_version = aiori_set_version, .fsync = MMAP_Fsync, .get_file_size = POSIX_GetFileSize, }; diff --git a/src/aiori-MPIIO.c b/src/aiori-MPIIO.c index d8c8d5e..7405ca5 100755 --- a/src/aiori-MPIIO.c +++ b/src/aiori-MPIIO.c @@ -377,7 +377,7 @@ static IOR_offset_t MPIIO_Xfer(int access, void *fd, IOR_size_t * buffer, */ static void MPIIO_Fsync(void *fdp, IOR_param_t * param) { - if (MPI_File_sync(*(MPI_File *)fd) != MPI_SUCCESS) + if (MPI_File_sync(*(MPI_File *)fdp) != MPI_SUCCESS) EWARN("fsync() failed"); } @@ -507,4 +507,4 @@ IOR_offset_t MPIIO_GetFileSize(IOR_param_t * test, MPI_Comm testComm, } return (aggFileSizeFromStat); -} \ No newline at end of file +} diff --git a/src/aiori-POSIX.c b/src/aiori-POSIX.c index 7e0dbdf..24d1d45 100755 --- a/src/aiori-POSIX.c +++ b/src/aiori-POSIX.c @@ -78,7 +78,7 @@ ior_aiori_t posix_aiori = { .xfer = POSIX_Xfer, .close = POSIX_Close, .delete = POSIX_Delete, - .set_version = POSIX_SetVersion, + .set_version = aiori_set_version, .fsync = POSIX_Fsync, .get_file_size = POSIX_GetFileSize, .statfs = aiori_posix_statfs, @@ -512,14 +512,6 @@ void POSIX_Delete(char *testFileName, IOR_param_t * param) EWARN(errmsg); } -/* - * Determine api version. - */ -void POSIX_SetVersion(IOR_param_t * test) -{ - strcpy(test->apiVersion, test->api); -} - /* * Use POSIX stat() to return aggregate file size. */ diff --git a/src/aiori.c b/src/aiori.c index c36de4a..8e3319e 100644 --- a/src/aiori.c +++ b/src/aiori.c @@ -112,6 +112,13 @@ int aiori_posix_stat (const char *path, struct stat *buf, IOR_param_t * param) return stat (path, buf); } +void aiori_set_version(IOR_param_t * test) +{ + strcpy(test->apiVersion, test->api); +} + + + const ior_aiori_t *aiori_select (const char *api) { char warn_str[256] = {0}; diff --git a/src/aiori.h b/src/aiori.h index 94c99e6..ca28c3c 100755 --- a/src/aiori.h +++ b/src/aiori.h @@ -96,12 +96,19 @@ int aiori_count (void); const char *aiori_default (void); /* some generic POSIX-based backend calls */ +void aiori_set_version(IOR_param_t * test); int aiori_posix_statfs (const char *path, ior_aiori_statfs_t *stat_buf, IOR_param_t * param); int aiori_posix_mkdir (const char *path, mode_t mode, IOR_param_t * param); int aiori_posix_rmdir (const char *path, IOR_param_t * param); int aiori_posix_access (const char *path, int mode, IOR_param_t * param); int aiori_posix_stat (const char *path, struct stat *buf, IOR_param_t * param); +void *POSIX_Create(char *testFileName, IOR_param_t * param); +void *POSIX_Open(char *testFileName, IOR_param_t * param); +IOR_offset_t POSIX_GetFileSize(IOR_param_t * test, MPI_Comm testComm, char *testFileName); +void POSIX_Delete(char *testFileName, IOR_param_t * param); +void POSIX_Close(void *fd, IOR_param_t * param); + /* NOTE: these 3 MPI-IO functions are exported for reuse by HDF5/PNetCDF */ void MPIIO_Delete(char *testFileName, IOR_param_t * param); IOR_offset_t MPIIO_GetFileSize(IOR_param_t * test, MPI_Comm testComm, diff --git a/src/ior.c b/src/ior.c index b53f867..36f13d6 100755 --- a/src/ior.c +++ b/src/ior.c @@ -1687,7 +1687,7 @@ static void ValidateTests(IOR_param_t * test) test, &defaults, singleXferAttempt); if (((strcasecmp(test->api, "POSIX") != 0) && (strcasecmp(test->api, "MPIIO") != 0) - && (strcasecmp(test->api, "MMAP") != 0) && + && (strcasecmp(test->api, "MMAP") != 0) && (strcasecmp(test->api, "HDFS") != 0)) && test->fsync) WARN_RESET("fsync() not supported in selected backend", test, &defaults, fsync); diff --git a/testing/basic-tests.sh b/testing/basic-tests.sh index 885a884..c81b129 100755 --- a/testing/basic-tests.sh +++ b/testing/basic-tests.sh @@ -15,7 +15,7 @@ MDTEST 2 -a POSIX -W 2 IOR 1 -a POSIX -w -z -F -Y -e -i1 -m -t 100k -b 1000k IOR 1 -a POSIX -w -z -F -k -e -i2 -m -t 100k -b 100k -IOR 1 -a POSIX -r -z -F -k -e -i1 -m -t 100k -b 100k +IOR 1 -a MMAP -r -z -F -k -e -i1 -m -t 100k -b 100k IOR 2 -a POSIX -w -z -C -F -k -e -i1 -m -t 100k -b 100k IOR 2 -a POSIX -w -z -C -Q 1 -F -k -e -i1 -m -t 100k -b 100k From 08351913726ef84a3a6c528230f3e6ddfdb15e05 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Thu, 12 Jul 2018 18:09:13 +0100 Subject: [PATCH 49/80] Option parser replaced. Benefits: shows currently set options when used with -h Supports options for child modules, example for aiori-dummy works, RADOS untested. --- src/Makefile.am | 6 +- src/aiori-DUMMY.c | 35 ++++- src/aiori-RADOS.c | 24 +++- src/aiori.h | 3 + src/mdtest.c | 211 ++++++++++++++---------------- src/option.c | 326 ++++++++++++++++++++++++++++++++++++++++++++++ src/option.h | 32 +++++ 7 files changed, 510 insertions(+), 127 deletions(-) create mode 100644 src/option.c create mode 100644 src/option.h diff --git a/src/Makefile.am b/src/Makefile.am index 2bb9e96..447b9b9 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,19 +3,19 @@ if USE_CAPS bin_PROGRAMS += IOR MDTEST endif -noinst_HEADERS = ior.h utilities.h parse_options.h aiori.h iordef.h getopt/optlist.h ior-internal.h +noinst_HEADERS = ior.h utilities.h parse_options.h aiori.h iordef.h getopt/optlist.h ior-internal.h option.h extraSOURCES = aiori.c aiori-DUMMY.c extraLDADD = extraLDFLAGS = extraCPPFLAGS = -ior_SOURCES = ior-main.c ior.c utilities.c parse_options.c getopt/optlist.c ior-output.c +ior_SOURCES = ior-main.c ior.c utilities.c parse_options.c getopt/optlist.c ior-output.c option.c ior_LDFLAGS = ior_LDADD = ior_CPPFLAGS = -mdtest_SOURCES = mdtest-main.c mdtest.c utilities.c getopt/optlist.c +mdtest_SOURCES = mdtest-main.c mdtest.c utilities.c getopt/optlist.c option.c mdtest_LDFLAGS = mdtest_LDADD = mdtest_CPPFLAGS = diff --git a/src/aiori-DUMMY.c b/src/aiori-DUMMY.c index eb54940..511a97c 100755 --- a/src/aiori-DUMMY.c +++ b/src/aiori-DUMMY.c @@ -14,16 +14,40 @@ #include "aiori.h" #include "utilities.h" + +/************************** O P T I O N S *****************************/ +struct dummy_options{ + uint64_t delay_creates; + int delay_rank_0_only; +}; + +static struct dummy_options o = { + .delay_creates = 0, + .delay_rank_0_only = 0, +}; + +static option_help options [] = { + {'c', "delay-create", "Delay per create in usec", OPTION_OPTIONAL_ARGUMENT, 'l', & o.delay_creates}, + {'z', "delay-only-rank0", "Delay only Rank0", OPTION_FLAG, 'd', & o.delay_rank_0_only}, + LAST_OPTION +}; + static char * current = (char*) 1; +static option_help * DUMMY_options(){ + return options; +} + static void *DUMMY_Create(char *testFileName, IOR_param_t * param) { if(verbose > 4){ fprintf(out_logfile, "DUMMY create: %s = %p\n", testFileName, current); } - if (rank == 0){ - usleep(100000); - } + if (o.delay_creates){ + if (! o.delay_rank_0_only || (o.delay_rank_0_only && rank == 0)){ + usleep(o.delay_creates); + } + } return current++; } @@ -58,7 +82,7 @@ static void DUMMY_Delete(char *testFileName, IOR_param_t * param) static void DUMMY_SetVersion(IOR_param_t * test) { - sprintf(test->apiVersion, "DUMMY"); + sprintf(test->apiVersion, "DUMMY-0.5"); } static IOR_offset_t DUMMY_GetFileSize(IOR_param_t * test, MPI_Comm testComm, char *testFileName) @@ -119,5 +143,6 @@ ior_aiori_t dummy_aiori = { DUMMY_mkdir, DUMMY_rmdir, DUMMY_access, - DUMMY_stat + DUMMY_stat, + DUMMY_options }; diff --git a/src/aiori-RADOS.c b/src/aiori-RADOS.c index 6d88a7c..a1f51c1 100755 --- a/src/aiori-RADOS.c +++ b/src/aiori-RADOS.c @@ -27,6 +27,21 @@ #include "aiori.h" #include "utilities.h" +/************************** O P T I O N S *****************************/ +struct rados_options{ + char * username; +}; + +static struct rados_options o = { + .username = "admin", +}; + +static option_help options [] = { + {'u', "username", "Username for the RADOS cluster", OPTION_REQUIRED_ARGUMENT, 's', & o.username}, + LAST_OPTION +}; + + /**************************** P R O T O T Y P E S *****************************/ static void *RADOS_Create(char *, IOR_param_t *); static void *RADOS_Open(char *, IOR_param_t *); @@ -42,9 +57,9 @@ static int RADOS_MkDir(const char *, mode_t, IOR_param_t *); static int RADOS_RmDir(const char *, IOR_param_t *); static int RADOS_Access(const char *, int, IOR_param_t *); static int RADOS_Stat(const char *, struct stat *, IOR_param_t *); +static option_help * RADIOS_options(); /************************** D E C L A R A T I O N S ***************************/ - ior_aiori_t rados_aiori = { .name = "RADOS", .create = RADOS_Create, @@ -60,6 +75,7 @@ ior_aiori_t rados_aiori = { .rmdir = RADOS_RmDir, .access = RADOS_Access, .stat = RADOS_Stat, + .get_options = RADIOS_options, }; #define RADOS_ERR(__err_str, __ret) do { \ @@ -68,14 +84,16 @@ ior_aiori_t rados_aiori = { } while(0) /***************************** F U N C T I O N S ******************************/ +static option_help * RADIOS_options(){ + return options; +} static void RADOS_Cluster_Init(IOR_param_t * param) { int ret; /* create RADOS cluster handle */ - /* XXX: HARDCODED RADOS USER NAME */ - ret = rados_create(¶m->rados_cluster, "admin"); + ret = rados_create(¶m->rados_cluster, o.username); if (ret) RADOS_ERR("unable to create RADOS cluster handle", ret); diff --git a/src/aiori.h b/src/aiori.h index ca28c3c..c815a9b 100755 --- a/src/aiori.h +++ b/src/aiori.h @@ -25,6 +25,7 @@ #include "ior.h" #include "iordef.h" /* IOR Definitions */ +#include "option.h" /*************************** D E F I N I T I O N S ****************************/ @@ -61,6 +62,7 @@ typedef struct ior_aiori_statfs { uint64_t f_ffree; } ior_aiori_statfs_t; + typedef struct ior_aiori { char *name; void *(*create)(char *, IOR_param_t *); @@ -77,6 +79,7 @@ typedef struct ior_aiori { int (*rmdir) (const char *path, IOR_param_t * param); int (*access) (const char *path, int mode, IOR_param_t * param); int (*stat) (const char *path, struct stat *buf, IOR_param_t * param); + option_help * (*get_options)(); } ior_aiori_t; extern ior_aiori_t dummy_aiori; diff --git a/src/mdtest.c b/src/mdtest.c index c88e0f9..2da9edc 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -38,7 +38,7 @@ #include #include -#include "getopt/optlist.h" +#include "option.h" #include "utilities.h" #if HAVE_SYS_PARAM_H @@ -1520,6 +1520,18 @@ void summarize_results(int iterations) { /* Checks to see if the test setup is valid. If it isn't, fail. */ void valid_tests() { + if(stone_wall_timer_seconds > 0 && branch_factor > 1 || ! barriers){ + fprintf(out_logfile, "Error, stone wall timer does only work with a branch factor <= 1 and with barriers\n"); + MPI_Abort(testComm, 1); + } + + if (!create_only && !stat_only && !read_only && !remove_only) { + create_only = stat_only = read_only = remove_only = 1; + if (( rank == 0 ) && ( verbose >= 1 )) { + fprintf( out_logfile, "V-1: main: Setting create/stat/read/remove_only to True\n" ); + fflush( out_logfile ); + } + } if (( rank == 0 ) && ( verbose >= 1 )) { fprintf( out_logfile, "V-1: Entering valid_tests...\n" ); @@ -2076,6 +2088,7 @@ void mdtest_init_args(){ nstride = 0; } + mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * world_out) { testComm = world_com; out_logfile = world_out; @@ -2097,14 +2110,76 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * int stride = 1; int iterations = 1; - /* Check for -h parameter before MPI_Init so the mdtest binary can be - called directly, without, for instance, mpirun. */ - for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { - print_help(); - } + verbose = 0; + int no_barriers = 0; + char * path = "./out"; + int randomize = 0; + option_help options [] = { + {'a', NULL, "API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI]", OPTION_OPTIONAL_ARGUMENT, 's', & backend_name}, + {'b', NULL, "branching factor of hierarchical directory structure", OPTION_OPTIONAL_ARGUMENT, 'd', & branch_factor}, + {'d', NULL, "the directory in which the tests will run", OPTION_OPTIONAL_ARGUMENT, 's', & path}, + {'B', NULL, "no barriers between phases", OPTION_OPTIONAL_ARGUMENT, 'd', & no_barriers}, + {'C', NULL, "only create files/dirs", OPTION_FLAG, 'd', & create_only}, + {'T', NULL, "only stat files/dirs", OPTION_FLAG, 'd', & stat_only}, + {'E', NULL, "only read files/dir", OPTION_FLAG, 'd', & read_only}, + {'r', NULL, "only remove files or directories left behind by previous runs", OPTION_FLAG, 'd', & remove_only}, + {'D', NULL, "perform test on directories only (no files)", OPTION_FLAG, 'd', & dirs_only}, + {'e', NULL, "bytes to read from each file", OPTION_OPTIONAL_ARGUMENT, 'l', & read_bytes}, + {'f', NULL, "first number of tasks on which the test will run", OPTION_OPTIONAL_ARGUMENT, 'd', & first}, + {'F', NULL, "perform test on files only (no directories)", OPTION_FLAG, 'd', & files_only}, + {'i', NULL, "number of iterations the test will run", OPTION_OPTIONAL_ARGUMENT, 'i', & iterations}, + {'I', NULL, "number of items per directory in tree", OPTION_OPTIONAL_ARGUMENT, 'l', & items_per_dir}, + {'l', NULL, "last number of tasks on which the test will run", OPTION_OPTIONAL_ARGUMENT, 'd', & last}, + {'L', NULL, "files only at leaf level of tree", OPTION_FLAG, 'd', & leaf_only}, + {'n', NULL, "every process will creat/stat/read/remove # directories and files", OPTION_OPTIONAL_ARGUMENT, 'l', & items}, + {'N', NULL, "stride # between neighbor tasks for file/dir operation (local=0)", OPTION_OPTIONAL_ARGUMENT, 'd', & nstride}, + {'p', NULL, "pre-iteration delay (in seconds)", OPTION_OPTIONAL_ARGUMENT, 'd', & pre_delay}, + {'R', NULL, "randomly stat files", OPTION_FLAG, 'd', & randomize}, + {0, "random-seed", "random seed for -R", OPTION_OPTIONAL_ARGUMENT, 'd', & random_seed}, + {'s', NULL, "stride between the number of tasks for each test", OPTION_OPTIONAL_ARGUMENT, 'd', & stride}, + {'S', NULL, "shared file access (file only, no directories)", OPTION_FLAG, 'd', & shared_file}, + {'c', NULL, "collective creates: task 0 does all creates", OPTION_FLAG, 'd', & collective_creates}, + {'t', NULL, "time unique working directory overhead", OPTION_FLAG, 'd', & time_unique_dir_overhead}, + {'u', NULL, "unique working directory for each task", OPTION_FLAG, 'd', & unique_dir_per_task}, + {'v', NULL, "verbosity (each instance of option increments by one)", OPTION_FLAG, 'd', & verbose}, + {'V', NULL, "verbosity value", OPTION_OPTIONAL_ARGUMENT, 'd', & verbose}, + {'w', NULL, "bytes to write to each file after it is created", OPTION_OPTIONAL_ARGUMENT, 'l', & write_bytes}, + {'W', NULL, "number in seconds; stonewall timer, write as many seconds and ensure all processes did the same number of operations (currently only stops during create phase)", OPTION_OPTIONAL_ARGUMENT, 'd', & stone_wall_timer_seconds}, + {'x', NULL, "StoneWallingStatusFile; contains the number of iterations of the creation phase, can be used to split phases across runs", OPTION_OPTIONAL_ARGUMENT, 's', & stoneWallingStatusFile}, + {'y', NULL, "sync file after writing", OPTION_FLAG, 'd', & sync_file}, + {'z', NULL, "depth of hierarchical directory structure", OPTION_OPTIONAL_ARGUMENT, 'd', & depth}, + {'Z', NULL, "print time instead of rate", OPTION_FLAG, 'd', & print_time}, + LAST_OPTION + }; + int printhelp = 0; + int parsed_options = option_parse(argc, argv, options, & printhelp); + + backend = aiori_select (backend_name); + if (NULL == backend) { + FAIL("Could not find suitable backend to use"); } + if(backend->get_options != NULL){ + option_parse(argc - parsed_options, argv + parsed_options, backend->get_options(), & printhelp); + } + + if(printhelp != 0){ + printf("\nSynopsis: %s ", argv[0]); + + option_print_help(options, 0); + + if(backend->get_options != NULL){ + printf("\nPlugin options for backend %s\n", backend_name); + option_print_help(backend->get_options(), 1); + } + if(printhelp == 1){ + exit(0); + }else{ + exit(1); + } + } + + MPI_Comm_rank(testComm, &rank); MPI_Comm_size(testComm, &size); @@ -2129,116 +2204,25 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * fflush(out_logfile); } - /* Parse command line options */ - - verbose = 0; - option_t *optList, *thisOpt; - optList = GetOptList(argc, argv, "a:b:BcCd:De:Ef:Fhi:I:l:Ln:N:p:rR::s:StTuvV:w:W:x:yz:Z"); - - - while (optList != NULL) { - thisOpt = optList; - optarg = thisOpt->argument; - optList = optList->next; - switch (thisOpt->option) { - case 'a': - backend_name = optarg; break; - case 'b': - branch_factor = atoi(optarg); break; - case 'B': - barriers = 0; break; - case 'c': - collective_creates = 1; break; - case 'C': - create_only = 1; break; - case 'd': - parse_dirpath(optarg); break; - case 'D': - dirs_only = 1; break; - case 'e': - read_bytes = ( size_t )strtoul( optarg, ( char ** )NULL, 10 ); break; - //read_bytes = atoi(optarg); break; - case 'E': - read_only = 1; break; - case 'f': - first = atoi(optarg); break; - case 'F': - files_only = 1; break; - case 'h': - print_help(); break; - case 'i': - iterations = atoi(optarg); break; - case 'I': - items_per_dir = (uint64_t) strtoul( optarg, ( char ** )NULL, 10 ); break; - //items_per_dir = atoi(optarg); break; - case 'l': - last = atoi(optarg); break; - case 'L': - leaf_only = 1; break; - case 'n': - items = (uint64_t) strtoul( optarg, ( char ** )NULL, 10 ); break; - //items = atoi(optarg); break; - case 'N': - nstride = atoi(optarg); break; - case 'p': - pre_delay = atoi(optarg); break; - case 'r': - remove_only = 1; break; - case 'R': - if (optarg == NULL) { - random_seed = time(NULL); - MPI_Barrier(testComm); - MPI_Bcast(&random_seed, 1, MPI_INT, 0, testComm); - random_seed += rank; - } else { - random_seed = atoi(optarg)+rank; - } - break; - case 's': - stride = atoi(optarg); break; - case 'S': - shared_file = 1; break; - case 't': - time_unique_dir_overhead = 1; break; - case 'T': - stat_only = 1; break; - case 'u': - unique_dir_per_task = 1; break; - case 'v': - verbose += 1; break; - case 'V': - verbose = atoi(optarg); break; - case 'w': - write_bytes = ( size_t )strtoul( optarg, ( char ** )NULL, 10 ); break; - case 'W': - stone_wall_timer_seconds = atoi( optarg ); break; - case 'x': - stoneWallingStatusFile = strdup(optarg); break; - case 'y': - sync_file = 1; break; - case 'z': - depth = atoi(optarg); break; - case 'Z': - print_time = TRUE; break; - } + /* adjust special variables */ + barriers = ! no_barriers; + if (path != NULL){ + parse_dirpath(path); } - - if(stone_wall_timer_seconds > 0 && branch_factor > 1 || ! barriers){ - fprintf(out_logfile, "Error, stone wall timer does only work with a branch factor <= 1 and with barriers\n"); - MPI_Abort(testComm, 1); - } - - if (!create_only && !stat_only && !read_only && !remove_only) { - create_only = stat_only = read_only = remove_only = 1; - if (( rank == 0 ) && ( verbose >= 1 )) { - fprintf( out_logfile, "V-1: main: Setting create/stat/read/remove_only to True\n" ); - fflush( out_logfile ); - } + if( randomize > 0 ){ + if (random_seed == 0) { + /* Ensure all procs have the same random number */ + random_seed = time(NULL); + MPI_Barrier(testComm); + MPI_Bcast(&random_seed, 1, MPI_INT, 0, testComm); + } + random_seed += rank; } valid_tests(); if (( rank == 0 ) && ( verbose >= 1 )) { + // option_print_current(options); fprintf (out_logfile, "api : %s\n", backend_name); fprintf( out_logfile, "barriers : %s\n", ( barriers ? "True" : "False" )); fprintf( out_logfile, "collective_creates : %s\n", ( collective_creates ? "True" : "False" )); @@ -2363,11 +2347,6 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * strcpy(testdirpath, filenames[rank%path_count]); } - backend = aiori_select (backend_name); - if (NULL == backend) { - FAIL("Could not find suitable backend to use"); - } - /* if directory does not exist, create it */ if ((rank < path_count) && backend->access(testdirpath, F_OK, ¶m) != 0) { if (backend->mkdir(testdirpath, DIRMODE, ¶m) != 0) { diff --git a/src/option.c b/src/option.c new file mode 100644 index 0000000..f9f6183 --- /dev/null +++ b/src/option.c @@ -0,0 +1,326 @@ +#include +#include +#include +#include +#include + +#include + +/* + * Initial revision by JK + */ + +static int print_value(option_help * o){ + int pos = 0; + if (o->arg == OPTION_OPTIONAL_ARGUMENT || o->arg == OPTION_REQUIRED_ARGUMENT){ + assert(o->variable != NULL); + + switch(o->type){ + case('F'):{ + pos += printf("=%.14f ", *(double*) o->variable); + break; + } + case('f'):{ + pos += printf("=%.6f ", (double) *(float*) o->variable); + break; + } + case('d'):{ + pos += printf("=%d ", *(int*) o->variable); + break; + } + case('H'): + case('s'):{ + if ( *(char**) o->variable != NULL && ((char**) o->variable)[0][0] != 0 ){ + pos += printf("=%s", *(char**) o->variable); + }else{ + pos += printf("=STRING"); + } + break; + } + case('c'):{ + pos += printf("=%c", *(char*) o->variable); + break; + } + case('l'):{ + pos += printf("=%lld", *(long long*) o->variable); + break; + } + } + } + if (o->arg == OPTION_FLAG && (*(int*)o->variable) != 0){ + pos += printf(" (%d)", (*(int*)o->variable)); + } + + return pos; +} + +static void print_help_section(option_help * args, option_value_type type, char * name){ + int first; + first = 1; + option_help * o; + for(o = args; o->shortVar != 0 || o->longVar != 0 || o->help != NULL ; o++){ + if( o->shortVar == 0 && o->longVar == 0 && o->help != NULL){ + printf("%-15s %s\n", "", o->help); + continue; + } + + if (o->arg == type){ + if (first){ + printf("\n%s\n", name); + first = 0; + } + printf(" "); + int pos = 0; + if(o->shortVar != 0 && o->longVar != 0){ + pos += printf("-%c, --%s", o->shortVar, o->longVar); + }else if(o->shortVar != 0){ + pos += printf("-%c", o->shortVar); + }else if(o->longVar != 0){ + pos += printf("--%s", o->longVar); + } + + pos += print_value(o); + if(o->help != NULL){ + for(int i = 0 ; i < (30 - pos); i++){ + printf(" "); + } + printf("%s", o->help); + } + printf("\n"); + } + } +} + +void option_print_help(option_help * args, int is_plugin){ + option_help * o; + int optionalArgs = 0; + for(o = args; o->shortVar != 0 || o->longVar != 0 ; o++){ + if(o->arg != OPTION_REQUIRED_ARGUMENT){ + optionalArgs = 1; + } + + switch(o->arg){ + case (OPTION_OPTIONAL_ARGUMENT): + case (OPTION_FLAG):{ + if(o->shortVar != 0){ + printf("[-%c] ", o->shortVar); + }else if(o->longVar != 0){ + printf("[--%s] ", o->longVar); + } + break; + }case (OPTION_REQUIRED_ARGUMENT):{ + if(o->shortVar != 0){ + printf("-%c ", o->shortVar); + }else if(o->longVar != 0){ + printf("--%s ", o->longVar); + } + break; + } + } + } + if (optionalArgs){ + //printf(" [Optional Args]"); + } + if (! is_plugin){ + printf(" -- \n"); + } + + print_help_section(args, OPTION_REQUIRED_ARGUMENT, "Required arguments"); + print_help_section(args, OPTION_FLAG, "Flags"); + print_help_section(args, OPTION_OPTIONAL_ARGUMENT, "Optional arguments"); +} + + +static int print_option_value(option_help * o){ + int pos = 0; + if (o->arg == OPTION_OPTIONAL_ARGUMENT || o->arg == OPTION_REQUIRED_ARGUMENT){ + assert(o->variable != NULL); + + switch(o->type){ + case('F'):{ + pos += printf("=%.14f ", *(double*) o->variable); + break; + } + case('f'):{ + pos += printf("=%.6f ", (double) *(float*) o->variable); + break; + } + case('d'):{ + pos += printf("=%d ", *(int*) o->variable); + break; + } + case('H'):{ + pos += printf("=HIDDEN"); + break; + } + case('s'):{ + if ( *(char**) o->variable != NULL && ((char**) o->variable)[0][0] != 0 ){ + pos += printf("=%s", *(char**) o->variable); + }else{ + pos += printf("="); + } + break; + } + case('c'):{ + pos += printf("=%c", *(char*) o->variable); + break; + } + case('l'):{ + pos += printf("=%lld", *(long long*) o->variable); + break; + } + } + }else{ + //printf(" "); + } + + return pos; +} + + +static void print_current_option_section(option_help * args, option_value_type type){ + option_help * o; + for(o = args; o->shortVar != 0 || o->longVar != 0 ; o++){ + if (o->arg == type){ + int pos = 0; + if (o->arg == OPTION_FLAG && (*(int*)o->variable) == 0){ + continue; + } + printf("\t"); + + if(o->shortVar != 0 && o->longVar != 0){ + pos += printf("%s", o->longVar); + }else if(o->shortVar != 0){ + pos += printf("%c", o->shortVar); + }else if(o->longVar != 0){ + pos += printf("%s", o->longVar); + } + + pos += print_option_value(o); + printf("\n"); + } + } +} + + +void option_print_current(option_help * args){ + print_current_option_section(args, OPTION_REQUIRED_ARGUMENT); + print_current_option_section(args, OPTION_OPTIONAL_ARGUMENT); + print_current_option_section(args, OPTION_FLAG); +} + +int option_parse(int argc, char ** argv, option_help * args, int * printhelp){ + int error = 0; + int requiredArgsSeen = 0; + int requiredArgsNeeded = 0; + int i; + + for(option_help * o = args; o->shortVar != 0 || o->longVar != 0 ; o++ ){ + if(o->arg == OPTION_REQUIRED_ARGUMENT){ + requiredArgsNeeded++; + } + } + for(i=1; i < argc; i++){ + char * txt = argv[i]; + int foundOption = 0; + char * arg = strstr(txt, "="); + if(arg != NULL){ + arg[0] = 0; + arg++; + } + if(strcmp(txt, "--") == 0){ + // we found plugin options + break; + } + + // try to find matching option help + for(option_help * o = args; o->shortVar != 0 || o->longVar != 0 || o->help != NULL ; o++ ){ + if ( (strlen(txt) == 2 && txt[0] == '-' && o->shortVar == txt[1]) || (strlen(txt) > 2 && txt[0] == '-' && txt[1] == '-' && o->longVar != NULL && strcmp(txt + 2, o->longVar) == 0)){ + foundOption = 1; + + if( o->shortVar == 0 && o->longVar == 0 && o->help != NULL){ + // section + continue; + } + + // now process the option. + switch(o->arg){ + case (OPTION_FLAG):{ + assert(o->type == 'd'); + (*(int*) o->variable)++; + break; + } + case (OPTION_OPTIONAL_ARGUMENT): + case (OPTION_REQUIRED_ARGUMENT):{ + // check if next is an argument + if(arg == NULL){ + // simply take the next value as argument + i++; + arg = argv[i]; + } + + switch(o->type){ + case('F'):{ + *(double*) o->variable = atof(arg); + break; + } + case('f'):{ + *(float*) o->variable = atof(arg); + break; + } + case('d'):{ + *(int*) o->variable = atoi(arg); + break; + } + case('H'): + case('s'):{ + (*(char **) o->variable) = strdup(arg); + break; + } + case('c'):{ + (*(char *)o->variable) = arg[0]; + if(strlen(arg) > 1){ + printf("Error, ignoring remainder of string for option %c (%s).\n", o->shortVar, o->longVar); + } + break; + } + case('l'):{ + *(long long*) o->variable = atoll(arg); + break; + } + } + } + } + if(arg != NULL){ + arg[-1] = '='; + } + + if(o->arg == OPTION_REQUIRED_ARGUMENT){ + requiredArgsSeen++; + } + + break; + } + } + if (! foundOption){ + if(strcmp(txt, "-h") == 0 || strcmp(txt, "--help") == 0){ + *printhelp=1; + }else{ + printf("Error invalid argument: %s\n", txt); + error = 1; + } + } + } + + if( requiredArgsSeen != requiredArgsNeeded ){ + printf("Error: Missing some required arguments\n\n"); + *printhelp = -1; + } + + if(error != 0){ + printf("Invalid options\n"); + *printhelp = -1; + } + + return i; +} diff --git a/src/option.h b/src/option.h new file mode 100644 index 0000000..e2f5249 --- /dev/null +++ b/src/option.h @@ -0,0 +1,32 @@ +#ifndef IOR_OPTION_H +#define IOR_OPTION_H + +/* + * Initial revision by JK + */ + +typedef enum{ + OPTION_FLAG, + OPTION_OPTIONAL_ARGUMENT, + OPTION_REQUIRED_ARGUMENT +} option_value_type; + +typedef struct{ + char shortVar; + char * longVar; + char * help; + + option_value_type arg; + char type; // data type, H = hidden string + void * variable; +} option_help; + +#define LAST_OPTION {0, 0, 0, (option_value_type) 0, 0, NULL} + +void option_print_help(option_help * args, int is_plugin); +void option_print_current(option_help * args); + +//@return the number of parsed arguments +int option_parse(int argc, char ** argv, option_help * args, int * print_help); + +#endif From d46402ff7c36d8f5fc09ac9f9f2448a485e36701 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Thu, 12 Jul 2018 19:33:08 +0100 Subject: [PATCH 50/80] Bugfix for SeLinux by Enno --- testing/docker/run-all-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/docker/run-all-tests.sh b/testing/docker/run-all-tests.sh index 466116f..172576f 100755 --- a/testing/docker/run-all-tests.sh +++ b/testing/docker/run-all-tests.sh @@ -12,7 +12,7 @@ mkdir -p $TARGET ARGS="$@" GID=$(id -g $USER) -OPT="-it --rm -v $PWD/../../:/data/" +OPT="-it --rm -v $PWD/../../:/data/:z" ERROR=0 VERBOSE=0 From 19a0102181a6fbfa7ea71768a3c2b6660e886274 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 14 Jul 2018 06:50:55 +0100 Subject: [PATCH 51/80] Silencing makefile by default. --- Makefile.am | 1 + configure.ac | 1 + 2 files changed, 2 insertions(+) diff --git a/Makefile.am b/Makefile.am index a9daf28..f01f362 100755 --- a/Makefile.am +++ b/Makefile.am @@ -1,3 +1,4 @@ +MAKEFLAGS = --no-print-directory SUBDIRS = src doc contrib EXTRA_DIST = META COPYRIGHT README.md ChangeLog # ACLOCAL_AMFLAGS needed for autoconf < 2.69 diff --git a/configure.ac b/configure.ac index 2e76042..a80908e 100755 --- a/configure.ac +++ b/configure.ac @@ -16,6 +16,7 @@ AC_CANONICAL_HOST # Automake support AM_INIT_AUTOMAKE([check-news dist-bzip2 gnu no-define foreign subdir-objects]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AM_MAINTAINER_MODE # Checks for programs From 54e47cf729721dba20f2de58fce9bd9091c47d00 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 14 Jul 2018 08:41:35 +0100 Subject: [PATCH 52/80] IOR: use new option parser. TODO: Parse "-O" options --- src/Makefile.am | 6 +- src/aiori-DUMMY.c | 6 +- src/aiori-MMAP.c | 2 +- src/aiori-MPIIO.c | 19 +- src/aiori-NCMPI.c | 6 +- src/aiori-POSIX.c | 2 +- src/aiori-RADOS.c | 9 +- src/aiori-S3.c | 44 +-- src/aiori.c | 6 +- src/aiori.h | 4 +- src/getopt/COPYING | 674 -------------------------------------- src/getopt/COPYING.LESSER | 165 ---------- src/getopt/README | 82 ----- src/getopt/optlist.c | 290 ---------------- src/getopt/optlist.h | 71 ---- src/getopt/sample.c | 103 ------ src/ior-output.c | 5 +- src/ior.c | 171 +++------- src/ior.h | 28 +- src/mdtest.c | 4 +- src/option.c | 77 ++++- src/option.h | 7 +- src/parse_options.c | 403 ++++++++--------------- 23 files changed, 303 insertions(+), 1881 deletions(-) delete mode 100644 src/getopt/COPYING delete mode 100644 src/getopt/COPYING.LESSER delete mode 100644 src/getopt/README delete mode 100644 src/getopt/optlist.c delete mode 100644 src/getopt/optlist.h delete mode 100644 src/getopt/sample.c diff --git a/src/Makefile.am b/src/Makefile.am index 447b9b9..9c27d89 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,19 +3,19 @@ if USE_CAPS bin_PROGRAMS += IOR MDTEST endif -noinst_HEADERS = ior.h utilities.h parse_options.h aiori.h iordef.h getopt/optlist.h ior-internal.h option.h +noinst_HEADERS = ior.h utilities.h parse_options.h aiori.h iordef.h ior-internal.h option.h extraSOURCES = aiori.c aiori-DUMMY.c extraLDADD = extraLDFLAGS = extraCPPFLAGS = -ior_SOURCES = ior-main.c ior.c utilities.c parse_options.c getopt/optlist.c ior-output.c option.c +ior_SOURCES = ior-main.c ior.c utilities.c parse_options.c ior-output.c option.c ior_LDFLAGS = ior_LDADD = ior_CPPFLAGS = -mdtest_SOURCES = mdtest-main.c mdtest.c utilities.c getopt/optlist.c option.c +mdtest_SOURCES = mdtest-main.c mdtest.c utilities.c option.c mdtest_LDFLAGS = mdtest_LDADD = mdtest_CPPFLAGS = diff --git a/src/aiori-DUMMY.c b/src/aiori-DUMMY.c index 511a97c..660a16b 100755 --- a/src/aiori-DUMMY.c +++ b/src/aiori-DUMMY.c @@ -80,9 +80,9 @@ static void DUMMY_Delete(char *testFileName, IOR_param_t * param) } } -static void DUMMY_SetVersion(IOR_param_t * test) +static char * DUMMY_getVersion() { - sprintf(test->apiVersion, "DUMMY-0.5"); + return "0.5"; } static IOR_offset_t DUMMY_GetFileSize(IOR_param_t * test, MPI_Comm testComm, char *testFileName) @@ -136,7 +136,7 @@ ior_aiori_t dummy_aiori = { DUMMY_Xfer, DUMMY_Close, DUMMY_Delete, - DUMMY_SetVersion, + DUMMY_getVersion, DUMMY_Fsync, DUMMY_GetFileSize, DUMMY_statfs, diff --git a/src/aiori-MMAP.c b/src/aiori-MMAP.c index ac622ff..f812bdd 100644 --- a/src/aiori-MMAP.c +++ b/src/aiori-MMAP.c @@ -42,7 +42,7 @@ ior_aiori_t mmap_aiori = { .xfer = MMAP_Xfer, .close = MMAP_Close, .delete = POSIX_Delete, - .set_version = aiori_set_version, + .get_version = aiori_get_version, .fsync = MMAP_Fsync, .get_file_size = POSIX_GetFileSize, }; diff --git a/src/aiori-MPIIO.c b/src/aiori-MPIIO.c index 7405ca5..d218b18 100755 --- a/src/aiori-MPIIO.c +++ b/src/aiori-MPIIO.c @@ -38,7 +38,7 @@ static void *MPIIO_Open(char *, IOR_param_t *); static IOR_offset_t MPIIO_Xfer(int, void *, IOR_size_t *, IOR_offset_t, IOR_param_t *); static void MPIIO_Close(void *, IOR_param_t *); -static void MPIIO_SetVersion(IOR_param_t *); +static char* MPIIO_GetVersion(); static void MPIIO_Fsync(void *, IOR_param_t *); @@ -51,7 +51,7 @@ ior_aiori_t mpiio_aiori = { .xfer = MPIIO_Xfer, .close = MPIIO_Close, .delete = MPIIO_Delete, - .set_version = MPIIO_SetVersion, + .get_version = MPIIO_GetVersion, .fsync = MPIIO_Fsync, .get_file_size = MPIIO_GetFileSize, .statfs = aiori_posix_statfs, @@ -411,13 +411,16 @@ void MPIIO_Delete(char *testFileName, IOR_param_t * param) /* * Determine api version. */ -static void MPIIO_SetVersion(IOR_param_t * test) +static char* MPIIO_GetVersion() { - int version, subversion; - MPI_CHECK(MPI_Get_version(&version, &subversion), - "cannot get MPI version"); - sprintf(test->apiVersion, "%s (version=%d, subversion=%d)", - test->api, version, subversion); + static char ver[1024] = {}; + if (ver){ + return ver; + } + int version, subversion; + MPI_CHECK(MPI_Get_version(&version, &subversion), "cannot get MPI version"); + sprintf(ver, "(%d.%d)", version, subversion); + return ver; } /* diff --git a/src/aiori-NCMPI.c b/src/aiori-NCMPI.c index d990dfd..42ee19e 100755 --- a/src/aiori-NCMPI.c +++ b/src/aiori-NCMPI.c @@ -67,7 +67,7 @@ ior_aiori_t ncmpi_aiori = { .xfer = NCMPI_Xfer, .close = NCMPI_Close, .delete = NCMPI_Delete, - .set_version = NCMPI_SetVersion, + .get_version = NCMPI_GetVersion, .fsync = NCMPI_Fsync, .get_file_size = NCMPI_GetFileSize, .statfs = aiori_posix_statfs, @@ -341,9 +341,9 @@ static void NCMPI_Delete(char *testFileName, IOR_param_t * param) /* * Determine api version. */ -static void NCMPI_SetVersion(IOR_param_t * test) +static char* NCMPI_GetVersion() { - sprintf(test->apiVersion, "%s (%s)", test->api, ncmpi_inq_libvers()); + return ncmpi_inq_libvers(); } /* diff --git a/src/aiori-POSIX.c b/src/aiori-POSIX.c index 24d1d45..9e740a6 100755 --- a/src/aiori-POSIX.c +++ b/src/aiori-POSIX.c @@ -78,7 +78,7 @@ ior_aiori_t posix_aiori = { .xfer = POSIX_Xfer, .close = POSIX_Close, .delete = POSIX_Delete, - .set_version = aiori_set_version, + .get_version = aiori_get_version, .fsync = POSIX_Fsync, .get_file_size = POSIX_GetFileSize, .statfs = aiori_posix_statfs, diff --git a/src/aiori-RADOS.c b/src/aiori-RADOS.c index a1f51c1..da27dec 100755 --- a/src/aiori-RADOS.c +++ b/src/aiori-RADOS.c @@ -49,7 +49,6 @@ static IOR_offset_t RADOS_Xfer(int, void *, IOR_size_t *, IOR_offset_t, IOR_param_t *); static void RADOS_Close(void *, IOR_param_t *); static void RADOS_Delete(char *, IOR_param_t *); -static void RADOS_SetVersion(IOR_param_t *); static void RADOS_Fsync(void *, IOR_param_t *); static IOR_offset_t RADOS_GetFileSize(IOR_param_t *, MPI_Comm, char *); static int RADOS_StatFS(const char *, ior_aiori_statfs_t *, IOR_param_t *); @@ -67,7 +66,7 @@ ior_aiori_t rados_aiori = { .xfer = RADOS_Xfer, .close = RADOS_Close, .delete = RADOS_Delete, - .set_version = RADOS_SetVersion, + .get_version = aiori_get_version, .fsync = RADOS_Fsync, .get_file_size = RADOS_GetFileSize, .statfs = RADOS_StatFS, @@ -257,12 +256,6 @@ static void RADOS_Delete(char *testFileName, IOR_param_t * param) return; } -static void RADOS_SetVersion(IOR_param_t * test) -{ - strcpy(test->apiVersion, test->api); - return; -} - static IOR_offset_t RADOS_GetFileSize(IOR_param_t * test, MPI_Comm testComm, char *testFileName) { diff --git a/src/aiori-S3.c b/src/aiori-S3.c index 08a4f75..7e45711 100755 --- a/src/aiori-S3.c +++ b/src/aiori-S3.c @@ -122,7 +122,6 @@ static IOR_offset_t EMC_Xfer(int, void*, IOR_size_t*, IOR_offset_t, IOR_param_t* static void EMC_Close(void*, IOR_param_t*); static void S3_Delete(char*, IOR_param_t*); -static void S3_SetVersion(IOR_param_t*); static void S3_Fsync(void*, IOR_param_t*); static IOR_offset_t S3_GetFileSize(IOR_param_t*, MPI_Comm, char*); @@ -138,7 +137,7 @@ ior_aiori_t s3_aiori = { .xfer = S3_Xfer, .close = S3_Close, .delete = S3_Delete, - .set_version = S3_SetVersion, + .get_version = aiori_get_version, .fsync = S3_Fsync, .get_file_size = S3_GetFileSize, }; @@ -183,7 +182,7 @@ ior_aiori_t s3_emc_aiori = { fflush(stdout); \ MPI_Abort((PARAM)->testComm, -1); \ } while (0) - + #define CURL_WARN(MSG, CURL_ERRNO) \ do { \ @@ -192,7 +191,7 @@ ior_aiori_t s3_emc_aiori = { __FILE__, __LINE__); \ fflush(stdout); \ } while (0) - + /* buffer is used to generate URLs, err_msgs, etc */ @@ -446,7 +445,7 @@ S3_Create_Or_Open_internal(char* testFileName, if ( n_to_n || (rank == 0) ) { // rank0 handles truncate - if ( needs_reset) { + if ( needs_reset) { aws_iobuf_reset(param->io_buf); AWS4C_CHECK( s3_put(param->io_buf, testFileName) ); /* 0-length write */ AWS4C_CHECK_OK( param->io_buf ); @@ -510,7 +509,7 @@ S3_Create_Or_Open_internal(char* testFileName, fprintf( stdout, "rank %d resetting\n", rank); } - + aws_iobuf_reset(param->io_buf); AWS4C_CHECK( s3_put(param->io_buf, testFileName) ); AWS4C_CHECK_OK( param->io_buf ); @@ -641,7 +640,7 @@ EMC_Open( char *testFileName, IOR_param_t * param ) { /* In the EMC case, instead of Multi-Part Upload we can use HTTP * "byte-range" headers to write parts of a single object. This appears to * have several advantages over the S3 MPU spec: - * + * * (a) no need for a special "open" operation, to capture an "UploadID". * Instead we simply write byte-ranges, and the server-side resolves * any races, producing a single winner. In the IOR case, there should @@ -808,7 +807,7 @@ S3_Xfer_internal(int access, printf("rank %d: part %d = ETag %s\n", rank, part_number, param->io_buf->eTag); } - // drop ptrs to , in param->io_buf + // drop ptrs to , in param->io_buf aws_iobuf_reset(param->io_buf); } else { // use EMC's byte-range write-support, instead of MPU @@ -830,7 +829,7 @@ S3_Xfer_internal(int access, AWS4C_CHECK ( s3_put(param->io_buf, file) ); AWS4C_CHECK_OK( param->io_buf ); - // drop ptrs to , in param->io_buf + // drop ptrs to , in param->io_buf aws_iobuf_reset(param->io_buf); } @@ -867,7 +866,7 @@ S3_Xfer_internal(int access, ERR_SIMPLE(buff); } - // drop refs to , in param->io_buf + // drop refs to , in param->io_buf aws_iobuf_reset(param->io_buf); } @@ -1126,7 +1125,7 @@ S3_Close_internal( void* fd, start_multiplier = ETAG_SIZE; /* one ETag */ stride = etag_data_size; /* one rank's-worth of Etag data */ } - + xml = aws_iobuf_new(); aws_iobuf_growth_size(xml, 1024 * 8); @@ -1305,7 +1304,7 @@ S3_Delete( char *testFileName, IOR_param_t * param ) { #if 0 // EMC BUG: If file was written with appends, and is deleted, // Then any future recreation will result in an object that can't be read. - // this + // this AWS4C_CHECK( s3_delete(param->io_buf, testFileName) ); #else // just replace with a zero-length object for now @@ -1334,7 +1333,7 @@ EMC_Delete( char *testFileName, IOR_param_t * param ) { #if 0 // EMC BUG: If file was written with appends, and is deleted, // Then any future recreation will result in an object that can't be read. - // this + // this AWS4C_CHECK( s3_delete(param->io_buf, testFileName) ); #else // just replace with a zero-length object for now @@ -1353,25 +1352,6 @@ EMC_Delete( char *testFileName, IOR_param_t * param ) { - -/* - * Determine API version. - */ - -static -void -S3_SetVersion( IOR_param_t * param ) { - if (param->verbose >= VERBOSE_2) { - printf("-> S3_SetVersion\n"); - } - - strcpy( param->apiVersion, param->api ); - - if (param->verbose >= VERBOSE_2) { - printf("<- S3_SetVersion\n"); - } -} - /* * HTTP HEAD returns meta-data for a "file". * diff --git a/src/aiori.c b/src/aiori.c index 8e3319e..2816bc1 100644 --- a/src/aiori.c +++ b/src/aiori.c @@ -112,13 +112,11 @@ int aiori_posix_stat (const char *path, struct stat *buf, IOR_param_t * param) return stat (path, buf); } -void aiori_set_version(IOR_param_t * test) +char* aiori_get_version() { - strcpy(test->apiVersion, test->api); + return ""; } - - const ior_aiori_t *aiori_select (const char *api) { char warn_str[256] = {0}; diff --git a/src/aiori.h b/src/aiori.h index c815a9b..afb763f 100755 --- a/src/aiori.h +++ b/src/aiori.h @@ -71,7 +71,7 @@ typedef struct ior_aiori { IOR_offset_t, IOR_param_t *); void (*close)(void *, IOR_param_t *); void (*delete)(char *, IOR_param_t *); - void (*set_version)(IOR_param_t *); + char* (*get_version)(); void (*fsync)(void *, IOR_param_t *); IOR_offset_t (*get_file_size)(IOR_param_t *, MPI_Comm, char *); int (*statfs) (const char *, ior_aiori_statfs_t *, IOR_param_t * param); @@ -99,7 +99,7 @@ int aiori_count (void); const char *aiori_default (void); /* some generic POSIX-based backend calls */ -void aiori_set_version(IOR_param_t * test); +char * aiori_get_version(); int aiori_posix_statfs (const char *path, ior_aiori_statfs_t *stat_buf, IOR_param_t * param); int aiori_posix_mkdir (const char *path, mode_t mode, IOR_param_t * param); int aiori_posix_rmdir (const char *path, IOR_param_t * param); diff --git a/src/getopt/COPYING b/src/getopt/COPYING deleted file mode 100644 index 94a9ed0..0000000 --- a/src/getopt/COPYING +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/src/getopt/COPYING.LESSER b/src/getopt/COPYING.LESSER deleted file mode 100644 index fc8a5de..0000000 --- a/src/getopt/COPYING.LESSER +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/src/getopt/README b/src/getopt/README deleted file mode 100644 index 662c082..0000000 --- a/src/getopt/README +++ /dev/null @@ -1,82 +0,0 @@ -DESCRIPTION ------------ -This archive contains the source code and supporting documentation for OptList, -an ANSI C command line option parser library. - -OptList is released under the GNU LGPL version 3.0. - -The latest revision of this program may be found at: -http://michael.dipperstein.com/optlist.html - -FILES ------ -COPYING - Rules for copying and distributing GNU GPL software -COPYING.LESSER - Rules for copying and distributing GNU LGPL software -optlist.c - Source code for the Optlist function and supporting - function. -optlist.h - Header file to be included by code using OptList -Makefile - Makefile for this project (assumes gcc compiler and GNU make) -README - This file -sample.c - A small program demonstrating how to use OptList - -BUILDING --------- -To build these files with GNU make and gcc: -1. Windows users should define the environment variable OS to be Windows or - Windows_NT. This is often already done. -2. Enter the command "make" from the command line. - -USAGE ------ -The file sample.c demonstrates the usage of OptList. - -SYNOPSIS -typedef struct option_t -{ - char option; - char *argument; - int argIndex; - struct option_t *next; -} option_t; - -option_t *GetOptList(int argc, char *const argv[], char *const options); - - -DESCRIPTION -The GetOptList() function is similar to getopt(). Its most notable differences -are that it returns a linked list to the command line arguments and their -parameters. One call to GetOptList() will return all of the command line -options and their arguments. GetOptList() will not modify argc or argv. - -GetOptList()'s parameters "argc" and "argv" are the argument count and array as -passed to the main() function on program invocation. An element of argv that -starts with "-" is an option element. The character following the "-" is option -an character. - -The parameter "options" is a string containing the legitimate option characters. -If such a character is followed by a colon, the option requires an argument. -(e.g. "a:bc?" a, b ,c, and, ? are all options. a should be followed by an -argument.) - -GetOptList() returns a linked list of type option_t. The "*next" field of the -element at the end of the list will be set to NULL. The "option" field will -contain the option character. A pointer to the following text in the same -argv-element, or the text of the following argv-element will be stored in the -"arguement" field, otherwise the "arguement" field is set to NULL. The index -of the argv-element containing the argument will be stored in the "argIndex". -If there is no argument, the field will contain OL_NOINDEX. - -HISTORY -------- -08/01/07 - Initial release -09/13/14 - Added FindFileName function, because I always use it with GetOptList - Tighter adherence to Michael Barr's "Top 10 Bug-Killing Coding - Standard Rules" (http://www.barrgroup.com/webinars/10rules). - -TODO ----- -- Add support for --option_name - -AUTHOR ------- -Michael Dipperstein (mdipper@alumni.engr.ucsb.edu) diff --git a/src/getopt/optlist.c b/src/getopt/optlist.c deleted file mode 100644 index 544855b..0000000 --- a/src/getopt/optlist.c +++ /dev/null @@ -1,290 +0,0 @@ -/*************************************************************************** -* Command Line Option Parser -* -* File : optlist.c -* Purpose : Provide getopt style command line option parsing -* Author : Michael Dipperstein -* Date : August 1, 2007 -* -**************************************************************************** -* -* OptList: A command line option parsing library -* Copyright (C) 2007, 2014 by -* Michael Dipperstein (mdipper@alumni.engr.ucsb.edu) -* -* This file is part of the OptList library. -* -* OptList is free software; you can redistribute it and/or modify it -* under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation; either version 3 of the License, or (at -* your option) any later version. -* -* OptList is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser -* General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this program. If not, see . -* -***************************************************************************/ - -/*************************************************************************** -* INCLUDED FILES -***************************************************************************/ -#include "optlist.h" -#include -#include -#include - -/*************************************************************************** -* TYPE DEFINITIONS -***************************************************************************/ - -/*************************************************************************** -* CONSTANTS -***************************************************************************/ - -/*************************************************************************** -* GLOBAL VARIABLES -***************************************************************************/ - -/*************************************************************************** -* PROTOTYPES -***************************************************************************/ -static option_t *MakeOpt( - const char option, char *const argument, const int index); - -static size_t MatchOpt( - const char argument, char *const options); - -/*************************************************************************** -* FUNCTIONS -***************************************************************************/ - -/**************************************************************************** -* Function : GetOptList -* Description: This function is similar to the POSIX function getopt. All -* options and their corresponding arguments are returned in a -* linked list. This function should only be called once per -* an option list and it does not modify argv or argc. -* Parameters : argc - the number of command line arguments (including the -* name of the executable) -* argv - pointer to the open binary file to write encoded -* output -* options - getopt style option list. A NULL terminated -* string of single character options. Follow an -* option with a colon to indicate that it requires -* an argument. -* Effects : Creates a link list of command line options and their -* arguments. -* Returned : option_t type value where the option and arguement fields -* contain the next option symbol and its argument (if any). -* The argument field will be set to NULL if the option is -* specified as having no arguments or no arguments are found. -* The option field will be set to PO_NO_OPT if no more -* options are found. -* -* NOTE: The caller is responsible for freeing up the option list when it -* is no longer needed. -****************************************************************************/ -option_t *GetOptList(const int argc, char *const argv[], char *const options) -{ - int nextArg; - option_t *head, *tail; - size_t optIndex; - size_t argIndex; - - /* start with first argument and nothing found */ - nextArg = 1; - head = NULL; - tail = NULL; - - /* loop through all of the command line arguments */ - while (nextArg < argc) - { - argIndex = 1; - - while ((strlen(argv[nextArg]) > argIndex) && ('-' == argv[nextArg][0])) - { - /* attempt to find a matching option */ - optIndex = MatchOpt(argv[nextArg][argIndex], options); - - if (options[optIndex] == argv[nextArg][argIndex]) - { - /* we found the matching option */ - if (NULL == head) - { - head = MakeOpt(options[optIndex], NULL, OL_NOINDEX); - tail = head; - } - else - { - tail->next = MakeOpt(options[optIndex], NULL, OL_NOINDEX); - tail = tail->next; - } - - if (':' == options[optIndex + 1]) - { - /* the option found should have a text arguement */ - argIndex++; - - if (strlen(argv[nextArg]) > argIndex) - { - /* no space between argument and option */ - tail->argument = &(argv[nextArg][argIndex]); - tail->argIndex = nextArg; - } - else if (nextArg < argc) - { - /* there must be space between the argument option */ - nextArg++; - tail->argument = argv[nextArg]; - tail->argIndex = nextArg; - } - - break; /* done with argv[nextArg] */ - } - } - - argIndex++; - } - - nextArg++; - } - - return head; -} - -/**************************************************************************** -* Function : MakeOpt -* Description: This function uses malloc to allocate space for an option_t -* type structure and initailizes the structure with the -* values passed as a parameter. -* Parameters : option - this option character -* argument - pointer string containg the argument for option. -* Use NULL for no argument -* index - argv[index] contains argument use OL_NOINDEX for -* no argument -* Effects : A new option_t type variable is created on the heap. -* Returned : Pointer to newly created and initialized option_t type -* structure. NULL if space for structure can't be allocated. -****************************************************************************/ -static option_t *MakeOpt( - const char option, char *const argument, const int index) -{ - option_t *opt; - - opt = malloc(sizeof(option_t)); - - if (opt != NULL) - { - opt->option = option; - opt->argument = argument; - opt->argIndex = index; - opt->next = NULL; - } - else - { - perror("Failed to Allocate option_t"); - } - - return opt; -} - -/**************************************************************************** -* Function : FreeOptList -* Description: This function will free all the elements in an option_t -* type linked list starting from the node passed as a -* parameter. -* Parameters : list - head of linked list to be freed -* Effects : All elements of the linked list pointed to by list will -* be freed and list will be set to NULL. -* Returned : None -****************************************************************************/ -void FreeOptList(option_t *list) -{ - option_t *head, *next; - - head = list; - list = NULL; - - while (head != NULL) - { - next = head->next; - free(head); - head = next; - } - - return; -} - -/**************************************************************************** -* Function : MatchOpt -* Description: This function searches for an arguement in an option list. -* It will return the index to the option matching the -* arguement or the index to the NULL if none is found. -* Parameters : arguement - character arguement to be matched to an -* option in the option list -* options - getopt style option list. A NULL terminated -* string of single character options. Follow an -* option with a colon to indicate that it requires -* an argument. -* Effects : None -* Returned : Index of argument in option list. Index of end of string -* if arguement does not appear in the option list. -****************************************************************************/ -static size_t MatchOpt( - const char argument, char *const options) -{ - size_t optIndex = 0; - - /* attempt to find a matching option */ - while ((options[optIndex] != '\0') && - (options[optIndex] != argument)) - { - do - { - optIndex++; - } - while ((options[optIndex] != '\0') && - (':' == options[optIndex])); - } - - return optIndex; -} - -/**************************************************************************** -* Function : FindFileName -* Description: This is function accepts a pointer to the name of a file -* along with path information and returns a pointer to the -* first character that is not part of the path. -* Parameters : fullPath - pointer to an array of characters containing -* a file name and possible path modifiers. -* Effects : None -* Returned : Returns a pointer to the first character after any path -* information. -****************************************************************************/ -char *FindFileName(const char *const fullPath) -{ - int i; - const char *start; /* start of file name */ - const char *tmp; - const char delim[3] = {'\\', '/', ':'}; /* path deliminators */ - - start = fullPath; - - /* find the first character after all file path delimiters */ - for (i = 0; i < 3; i++) - { - tmp = strrchr(start, delim[i]); - - if (tmp != NULL) - { - start = tmp + 1; - } - } - - return (char *)start; -} - diff --git a/src/getopt/optlist.h b/src/getopt/optlist.h deleted file mode 100644 index c8d7af7..0000000 --- a/src/getopt/optlist.h +++ /dev/null @@ -1,71 +0,0 @@ -/*************************************************************************** -* Command Line Option Parser -* -* File : optlist.h -* Purpose : Header for getopt style command line option parsing -* Author : Michael Dipperstein -* Date : August 1, 2007 -* -**************************************************************************** -* -* OptList: A command line option parsing library -* Copyright (C) 2007, 20014 by -* Michael Dipperstein (mdipper@alumni.engr.ucsb.edu) -* -* This file is part of the OptList library. -* -* OptList is free software; you can redistribute it and/or modify it -* under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation; either version 3 of the License, or (at -* your option) any later version. -* -* OptList is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser -* General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this program. If not, see . -* -***************************************************************************/ -#ifndef OPTLIST_H -#define OPTLIST_H - -/*************************************************************************** -* INCLUDED FILES -***************************************************************************/ - -/*************************************************************************** -* MACROS -***************************************************************************/ - -/*************************************************************************** -* CONSTANTS -***************************************************************************/ -#define OL_NOINDEX -1 /* this option has no arguement */ - -/*************************************************************************** -* TYPE DEFINITIONS -***************************************************************************/ -typedef struct option_t -{ - char option; /* the current character option character */ - char *argument; /* pointer to arguments for this option */ - int argIndex; /* index into argv[] containing the argument */ - struct option_t *next; /* the next option in the linked list */ -} option_t; - -/*************************************************************************** -* PROTOTYPES -***************************************************************************/ - -/* returns a linked list of options and arguments similar to getopt() */ -option_t *GetOptList(int argc, char *const argv[], char *const options); - -/* frees the linked list of option_t returned by GetOptList */ -void FreeOptList(option_t *list); - -/* return a pointer to file name in a full path. useful for argv[0] */ -char *FindFileName(const char *const fullPath); - -#endif /* ndef OPTLIST_H */ diff --git a/src/getopt/sample.c b/src/getopt/sample.c deleted file mode 100644 index d589515..0000000 --- a/src/getopt/sample.c +++ /dev/null @@ -1,103 +0,0 @@ -/*************************************************************************** -* OptList Usage Sample -* -* File : sample.c -* Purpose : Demonstrates usage of optlist library. -* Author : Michael Dipperstein -* Date : July 23, 2004 -* -**************************************************************************** -* -* Sample: A optlist library sample usage program -* Copyright (C) 2007, 2014 by -* Michael Dipperstein (mdipper@alumni.engr.ucsb.edu) -* -* This file is part of the optlist library. -* -* The optlist library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public License as -* published by the Free Software Foundation; either version 3 of the -* License, or (at your option) any later version. -* -* The optlist library is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser -* General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this program. If not, see . -* -***************************************************************************/ - -/*************************************************************************** -* INCLUDED FILES -***************************************************************************/ -#include -#include -#include "optlist.h" - -/*************************************************************************** -* PROTOTYPES -***************************************************************************/ - -/*************************************************************************** -* FUNCTIONS -***************************************************************************/ - -/**************************************************************************** -* Function : main -* Description: This is the main function for this program, it calls -* optlist to parse the command line input displays the -* results of the parsing. -* Parameters : argc - number of parameters -* argv - parameter list -* Effects : parses command line parameters -* Returned : EXIT_SUCCESS for success, otherwise EXIT_FAILURE. -****************************************************************************/ -int main(int argc, char *argv[]) -{ - option_t *optList, *thisOpt; - - /* get list of command line options and their arguments */ - optList = NULL; - optList = GetOptList(argc, argv, "a:bcd:ef?"); - - /* display results of parsing */ - while (optList != NULL) - { - thisOpt = optList; - optList = optList->next; - - if ('?' == thisOpt->option) - { - printf("Usage: %s \n\n", FindFileName(argv[0])); - printf("options:\n"); - printf(" -a : option excepting argument.\n"); - printf(" -b : option without arguments.\n"); - printf(" -c : option without arguments.\n"); - printf(" -d : option excepting argument.\n"); - printf(" -e : option without arguments.\n"); - printf(" -f : option without arguments.\n"); - printf(" -? : print out command line options.\n\n"); - - FreeOptList(thisOpt); /* free the rest of the list */ - return EXIT_SUCCESS; - } - - printf("found option %c\n", thisOpt->option); - - if (thisOpt->argument != NULL) - { - printf("\tfound argument %s", thisOpt->argument); - printf(" at index %d\n", thisOpt->argIndex); - } - else - { - printf("\tno argument for this option\n"); - } - - free(thisOpt); /* done with this item, free it */ - } - - return EXIT_SUCCESS; -} diff --git a/src/ior-output.c b/src/ior-output.c index 8a88876..0bb3d5a 100644 --- a/src/ior-output.c +++ b/src/ior-output.c @@ -78,7 +78,7 @@ static void PrintKeyValEnd(){ } static void PrintKeyVal(char * key, char * value){ - if(value[strlen(value) -1 ] == '\n'){ + if(value[0] != 0 && value[strlen(value) -1 ] == '\n'){ // remove \n value[strlen(value) -1 ] = 0; } @@ -352,7 +352,6 @@ void ShowTestStart(IOR_param_t *test) PrintKeyValInt("useExistingTestFile", test->useExistingTestFile); PrintKeyValInt("showHints", test->showHints); PrintKeyValInt("uniqueDir", test->uniqueDir); - PrintKeyValInt("showHelp", test->showHelp); PrintKeyValInt("individualDataSets", test->individualDataSets); PrintKeyValInt("singleXferAttempt", test->singleXferAttempt); PrintKeyValInt("readFile", test->readFile); @@ -407,7 +406,7 @@ void ShowTestEnd(IOR_test_t *tptr){ */ void ShowSetup(IOR_param_t *params) { - if (strcmp(params->debug, "") != 0) { + if (params->debug) { fprintf(out_logfile, "\n*** DEBUG MODE ***\n"); fprintf(out_logfile, "*** %s ***\n\n", params->debug); } diff --git a/src/ior.c b/src/ior.c index 36f13d6..4cfd821 100755 --- a/src/ior.c +++ b/src/ior.c @@ -43,10 +43,9 @@ static int totalErrorCount; static const ior_aiori_t *backend; static void DestroyTests(IOR_test_t *tests_head); -static void DisplayUsage(char **); static char *PrependDir(IOR_param_t *, char *); static char **ParseFileName(char *, int *); -static IOR_test_t *SetupTests(int, char **); +static void InitTests(IOR_test_t * , MPI_Comm); static void TestIoSys(IOR_test_t *); static void ValidateTests(IOR_param_t *); static IOR_offset_t WriteOrRead(IOR_param_t * test, IOR_results_t * results, void *fd, int access, IOR_io_buffers* ioBuffers); @@ -70,14 +69,9 @@ IOR_test_t * ior_run(int argc, char **argv, MPI_Comm world_com, FILE * world_out } /* setup tests, and validate parameters */ - tests_head = SetupTests(argc, argv); + tests_head = ParseCommandLine(argc, argv); + InitTests(tests_head, world_com); verbose = tests_head->params.verbose; - tests_head->params.testComm = world_com; - - /* check for commandline 'help' request */ - if (rank == 0 && tests_head->params.showHelp == TRUE) { - DisplayUsage(argv); - } PrintHeader(argc, argv); @@ -85,7 +79,6 @@ IOR_test_t * ior_run(int argc, char **argv, MPI_Comm world_com, FILE * world_out for (tptr = tests_head; tptr != NULL; tptr = tptr->next) { totalErrorCount = 0; verbose = tptr->params.verbose; - tptr->params.testComm = world_com; if (rank == 0 && verbose >= VERBOSE_0) { ShowTestStart(&tptr->params); } @@ -114,15 +107,8 @@ int ior_main(int argc, char **argv) /* * check -h option from commandline without starting MPI; - * if the help option is requested in a script file (showHelp=TRUE), - * the help output will be displayed in the MPI job */ - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-h") == 0) { - DisplayUsage(argv); - return (0); - } - } + tests_head = ParseCommandLine(argc, argv); #ifdef USE_S3_AIORI /* This is supposed to be done before *any* threads are created. @@ -152,18 +138,8 @@ int ior_main(int argc, char **argv) } /* setup tests, and validate parameters */ - tests_head = SetupTests(argc, argv); + InitTests(tests_head, mpi_comm_world); verbose = tests_head->params.verbose; - tests_head->params.testComm = mpi_comm_world; - - /* check for commandline 'help' request */ - if (tests_head->params.showHelp == TRUE) { - if( rank == 0 ){ - DisplayUsage(argv); - } - MPI_Finalize(); - exit(0); - } PrintHeader(argc, argv); @@ -223,9 +199,12 @@ void init_IOR_Param_t(IOR_param_t * p) p->mode = IOR_IRUSR | IOR_IWUSR | IOR_IRGRP | IOR_IWGRP; p->openFlags = IOR_RDWR | IOR_CREAT; - strncpy(p->api, default_aiori, MAX_STR); - strncpy(p->platform, "HOST(OSTYPE)", MAX_STR); - strncpy(p->testFileName, "testFile", MAX_PATHLEN); + p->api = strdup(default_aiori); + p->platform = strdup("HOST(OSTYPE)"); + p->testFileName = strdup("testFile"); + + p->writeFile = p->readFile = FALSE; + p->checkWrite = p->checkRead = FALSE; p->nodes = 1; p->tasksPerNode = 1; @@ -244,8 +223,8 @@ void init_IOR_Param_t(IOR_param_t * p) hdfs_user = getenv("USER"); if (!hdfs_user) - hdfs_user = ""; - strncpy(p->hdfs_user, hdfs_user, MAX_STR); + hdfs_user = ""; + p->hdfs_user = strdup(hdfs_user); p->hdfs_name_node = "default"; p->hdfs_name_node_port = 0; /* ??? */ p->hdfs_fs = NULL; @@ -557,7 +536,7 @@ static void aligned_buffer_free(void *buf) free(*(void **)((char *)buf - sizeof(char *))); } -void AllocResults(IOR_test_t *test) +static void AllocResults(IOR_test_t *test) { int reps; if (test->results != NULL) @@ -619,12 +598,12 @@ IOR_test_t *CreateTest(IOR_param_t *init_params, int test_num) if (newTest == NULL) ERR("malloc() of IOR_test_t failed"); newTest->params = *init_params; - GetPlatformName(newTest->params.platform); - newTest->params.nodes = init_params->numTasks / tasksPerNode; - newTest->params.tasksPerNode = tasksPerNode; + newTest->params.platform = GetPlatformName(); newTest->params.id = test_num; newTest->next = NULL; newTest->results = NULL; + + AllocResults(newTest); return newTest; } @@ -644,82 +623,6 @@ static void DestroyTests(IOR_test_t *tests_head) } } -/* - * Display usage of script file. - */ -static void DisplayUsage(char **argv) -{ - char *opts[] = { - "OPTIONS:", - " -a S api -- API for I/O [POSIX|MMAP|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI]", - " -A N refNum -- user supplied reference number to include in the summary", - " -b N blockSize -- contiguous bytes to write per task (e.g.: 8, 4k, 2m, 1g)", - " -B useO_DIRECT -- uses O_DIRECT for POSIX, bypassing I/O buffers", - " -c collective -- collective I/O", - " -C reorderTasks -- changes task ordering to n+1 ordering for readback", - " -d N interTestDelay -- delay between reps in seconds", - " -D N deadlineForStonewalling -- seconds before stopping write or read phase", - " -O stoneWallingWearOut=1 -- once the stonewalling timout is over, all process finish to access the amount of data", - " -O stoneWallingWearOutIterations=N -- stop after processing this number of iterations, needed for reading data back written with stoneWallingWearOut", - " -O stoneWallingStatusFile=FILE -- this file keeps the number of iterations from stonewalling during write and allows to use them for read", - " -e fsync -- perform fsync/msync upon POSIX/MMAP write close", - " -E useExistingTestFile -- do not remove test file before write access", - " -f S scriptFile -- test script name", - " -F filePerProc -- file-per-process", - " -g intraTestBarriers -- use barriers between open, write/read, and close", - " -G N setTimeStampSignature -- set value for time stamp signature/random seed", - " -h showHelp -- displays options and help", - " -H showHints -- show hints", - " -i N repetitions -- number of repetitions of test", - " -I individualDataSets -- datasets not shared by all procs [not working]", - " -j N outlierThreshold -- warn on outlier N seconds from mean", - " -J N setAlignment -- HDF5 alignment in bytes (e.g.: 8, 4k, 2m, 1g)", - " -k keepFile -- don't remove the test file(s) on program exit", - " -K keepFileWithError -- keep error-filled file(s) after data-checking", - " -l datapacket type-- type of packet that will be created [offset|incompressible|timestamp|o|i|t]", - " -m multiFile -- use number of reps (-i) for multiple file count", - " -M N memoryPerNode -- hog memory on the node (e.g.: 2g, 75%)", - " -n noFill -- no fill in HDF5 file creation", - " -N N numTasks -- number of tasks that should participate in the test", - " -o S testFile -- full name for test", - " -O S string of IOR directives (e.g. -O checkRead=1,lustreStripeCount=32)", - " -p preallocate -- preallocate file size", - " -P useSharedFilePointer -- use shared file pointer [not working]", - " -q quitOnError -- during file error-checking, abort on error", - " -Q N taskPerNodeOffset for read tests use with -C & -Z options (-C constant N, -Z at least N)", - " -r readFile -- read existing file", - " -R checkRead -- verify that the output of read matches the expected signature (used with -G)", - " -s N segmentCount -- number of segments", - " -S useStridedDatatype -- put strided access into datatype [not working]", - " -t N transferSize -- size of transfer in bytes (e.g.: 8, 4k, 2m, 1g)", - " -T N maxTimeDuration -- max time in minutes executing repeated test; it aborts only between iterations and not within a test!", - " -u uniqueDir -- use unique directory name for each file-per-process", - " -U S hintsFileName -- full name for hints file", - " -v verbose -- output information (repeating flag increases level)", - " -V useFileView -- use MPI_File_set_view", - " -w writeFile -- write file", - " -W checkWrite -- check read after write", - " -x singleXferAttempt -- do not retry transfer if incomplete", - " -X N reorderTasksRandomSeed -- random seed for -Z option", - " -Y fsyncPerWrite -- perform fsync/msync after each POSIX/MMAP write", - " -z randomOffset -- access is to random, not sequential, offsets within a file", - " -Z reorderTasksRandom -- changes task ordering to random ordering for readback", - " -O summaryFile=FILE -- store result data into this file", - " -O summaryFormat=[default,JSON,CSV] -- use the format for outputing the summary", - " ", - " NOTE: S is a string, N is an integer number.", - " ", - "" - }; - int i = 0; - - fprintf(out_logfile, "Usage: %s [OPTIONS]\n\n", *argv); - for (i = 0; strlen(opts[i]) > 0; i++) - fprintf(out_logfile, "%s\n", opts[i]); - - return; -} - /* * Distribute IOR_HINTs to all tasks' environments. */ @@ -823,9 +726,10 @@ FillBuffer(void *buffer, /* * Return string describing machine name and type. */ -void GetPlatformName(char *platformName) +char * GetPlatformName() { char nodeName[MAX_STR], *p, *start, sysName[MAX_STR]; + char platformName[MAX_STR]; struct utsname name; if (uname(&name) != 0) { @@ -858,6 +762,7 @@ void GetPlatformName(char *platformName) } sprintf(platformName, "%s(%s)", nodeName, sysName); + return strdup(platformName); } @@ -1097,14 +1002,16 @@ static void RemoveFile(char *testFileName, int filePerProc, IOR_param_t * test) * Setup tests by parsing commandline and creating test script. * Perform a sanity-check on the configured parameters. */ -static IOR_test_t *SetupTests(int argc, char **argv) +static void InitTests(IOR_test_t *tests, MPI_Comm com) { - IOR_test_t *tests, *testsHead; + IOR_test_t *testsHead = tests; + int size; + + MPI_CHECK(MPI_Comm_size(com, & size), "MPI_Comm_size() error"); /* count the tasks per node */ - tasksPerNode = CountTasksPerNode(mpi_comm_world); + tasksPerNode = CountTasksPerNode(com); - testsHead = tests = ParseCommandLine(argc, argv); /* * Since there is no guarantee that anyone other than * task 0 has the environment settings for the hints, pass @@ -1114,6 +1021,16 @@ static IOR_test_t *SetupTests(int argc, char **argv) /* check validity of tests and create test queue */ while (tests != NULL) { + IOR_param_t *params = & tests->params; + params->testComm = com; + params->nodes = params->numTasks / tasksPerNode; + params->tasksPerNode = tasksPerNode; + if (params->numTasks == 0) { + params->numTasks = size; + } + params->expectedAggFileSize = + params->blockSize * params->segmentCount * params->numTasks; + ValidateTests(&tests->params); tests = tests->next; } @@ -1122,8 +1039,6 @@ static IOR_test_t *SetupTests(int argc, char **argv) /* seed random number generator */ SeedRandGen(mpi_comm_world); - - return (testsHead); } /* @@ -1468,8 +1383,7 @@ static void TestIoSys(IOR_test_t *test) } if (params->reorderTasks) { /* move two nodes away from writing node */ - rankOffset = - (2 * params->tasksPerNode) % params->numTasks; + rankOffset = (2 * params->tasksPerNode) % params->numTasks; } // update the check buffer @@ -1497,9 +1411,8 @@ static void TestIoSys(IOR_test_t *test) /* Constant process offset reading */ if (params->reorderTasks) { /* move taskPerNodeOffset nodes[1==default] away from writing node */ - rankOffset = - (params->taskPerNodeOffset * - params->tasksPerNode) % params->numTasks; + rankOffset = (params->taskPerNodeOffset * + params->tasksPerNode) % params->numTasks; } /* random process offset reading */ if (params->reorderTasksRandom) { @@ -1626,10 +1539,6 @@ static void ValidateTests(IOR_param_t * test) IOR_param_t defaults; init_IOR_Param_t(&defaults); - /* get the version of the tests */ - AioriBind(test->api, test); - backend->set_version(test); - if (test->repetitions <= 0) WARN_RESET("too few test repetitions", test, &defaults, repetitions); @@ -2005,7 +1914,7 @@ static IOR_offset_t WriteOrRead(IOR_param_t * test, IOR_results_t * results, voi && ((GetTimeStamp() - startForStonewall) > test->deadlineForStonewalling)); - if(access == READ && test->stoneWallingStatusFile[0]){ + if(access == READ && test->stoneWallingStatusFile){ test->stoneWallingWearOutIterations = ReadStoneWallingIterations(test->stoneWallingStatusFile); if(test->stoneWallingWearOutIterations == -1){ ERR("Could not read back the stonewalling status from the file!"); diff --git a/src/ior.h b/src/ior.h index e8e465a..b6655b3 100755 --- a/src/ior.h +++ b/src/ior.h @@ -87,17 +87,18 @@ typedef struct IO_BUFFERS typedef struct { - char debug[MAX_STR]; /* debug info string */ + const void * backend; + char * debug; /* debug info string */ unsigned int mode; /* file permissions */ unsigned int openFlags; /* open flags (see also ) */ int referenceNumber; /* user supplied reference number */ - char api[MAX_STR]; /* API for I/O */ - char apiVersion[MAX_STR]; /* API version */ - char platform[MAX_STR]; /* platform type */ - char testFileName[MAXPATHLEN]; /* full name for test */ - char testFileName_fppReadCheck[MAXPATHLEN];/* filename for fpp read check */ - char hintsFileName[MAXPATHLEN]; /* full name for hints file */ - char options[MAXPATHLEN]; /* options string */ + char * api; /* API for I/O */ + char * apiVersion; /* API version */ + char * platform; /* platform type */ + char * testFileName; /* full name for test */ + char * testFileName_fppReadCheck;/* filename for fpp read check */ + char * hintsFileName; /* full name for hints file */ + char * options; /* options string */ int numTasks; /* number of tasks for test */ int nodes; /* number of nodes for test */ int tasksPerNode; /* number of tasks per node */ @@ -131,7 +132,6 @@ typedef struct int useStridedDatatype; /* put strided access into datatype */ int useO_DIRECT; /* use O_DIRECT, bypassing I/O buffers */ int showHints; /* show hints */ - int showHelp; /* show options and help */ int summary_every_test; /* flag to print summary every test, not just at end */ int uniqueDir; /* use unique directory for each fpp */ int useExistingTestFile; /* do not delete test file before access */ @@ -139,7 +139,7 @@ typedef struct int deadlineForStonewalling; /* max time in seconds to run any test phase */ int stoneWallingWearOut; /* wear out the stonewalling, once the timout is over, each process has to write the same amount */ uint64_t stoneWallingWearOutIterations; /* the number of iterations for the stonewallingWearOut, needed for readBack */ - char stoneWallingStatusFile[MAXPATHLEN]; + char * stoneWallingStatusFile; int maxTimeDuration; /* max time in minutes to run each test */ int outlierThreshold; /* warn on outlier N seconds from mean */ @@ -173,7 +173,7 @@ typedef struct IOR_offset_t setAlignment; /* alignment in bytes */ /* HDFS variables */ - char hdfs_user[MAX_STR]; /* copied from ENV, for now */ + char * hdfs_user; /* copied from ENV, for now */ const char* hdfs_name_node; tPort hdfs_name_node_port; /* (uint16_t) */ hdfsFS hdfs_fs; /* file-system handle */ @@ -190,8 +190,7 @@ typedef struct IOBuf* io_buf; /* aws4c places parsed header values here */ IOBuf* etags; /* accumulate ETags for N:1 parts */ size_t part_number; /* multi-part upload increment (PER-RANK!) */ -# define MAX_UPLOAD_ID_SIZE 256 /* seems to be 32, actually */ - char UploadId[MAX_UPLOAD_ID_SIZE +1]; /* key for multi-part-uploads */ + char* UploadId; /* key for multi-part-uploads */ /* RADOS variables */ rados_t rados_cluster; /* RADOS cluster handle */ @@ -245,8 +244,7 @@ typedef struct IOR_test_t { IOR_test_t *CreateTest(IOR_param_t *init_params, int test_num); -void AllocResults(IOR_test_t *test); -void GetPlatformName(char *); +char * GetPlatformName(); void init_IOR_Param_t(IOR_param_t *p); /* diff --git a/src/mdtest.c b/src/mdtest.c index 2da9edc..53250e2 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -2154,7 +2154,7 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * int printhelp = 0; int parsed_options = option_parse(argc, argv, options, & printhelp); - backend = aiori_select (backend_name); + backend = aiori_select(backend_name); if (NULL == backend) { FAIL("Could not find suitable backend to use"); } @@ -2164,7 +2164,7 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * } if(printhelp != 0){ - printf("\nSynopsis: %s ", argv[0]); + printf("Usage: %s ", argv[0]); option_print_help(options, 0); diff --git a/src/option.c b/src/option.c index f9f6183..13b239d 100644 --- a/src/option.c +++ b/src/option.c @@ -6,6 +6,45 @@ #include +/* +* Takes a string of the form 64, 8m, 128k, 4g, etc. and converts to bytes. +*/ +int64_t string_to_bytes(char *size_str) +{ + int64_t size = 0; + char range; + int rc; + + rc = sscanf(size_str, " %lld %c ", (long long*) & size, &range); + if (rc == 2) { + switch ((int)range) { + case 'k': + case 'K': + size <<= 10; + break; + case 'm': + case 'M': + size <<= 20; + break; + case 'g': + case 'G': + size <<= 30; + break; + case 't': + case 'T': + size <<= 40; + break; + case 'p': + case 'P': + size <<= 50; + break; + } + } else if (rc == 0) { + size = -1; + } + return (size); +} + /* * Initial revision by JK */ @@ -16,6 +55,10 @@ static int print_value(option_help * o){ assert(o->variable != NULL); switch(o->type){ + case('p'):{ + pos += printf("=STRING"); + break; + } case('F'):{ pos += printf("=%.14f ", *(double*) o->variable); break; @@ -59,12 +102,12 @@ static void print_help_section(option_help * args, option_value_type type, char first = 1; option_help * o; for(o = args; o->shortVar != 0 || o->longVar != 0 || o->help != NULL ; o++){ - if( o->shortVar == 0 && o->longVar == 0 && o->help != NULL){ - printf("%-15s %s\n", "", o->help); - continue; - } if (o->arg == type){ + if( o->shortVar == 0 && o->longVar == 0 && o->help != NULL){ + printf("%s\n", o->help); + continue; + } if (first){ printf("\n%s\n", name); first = 0; @@ -235,13 +278,13 @@ int option_parse(int argc, char ** argv, option_help * args, int * printhelp){ // try to find matching option help for(option_help * o = args; o->shortVar != 0 || o->longVar != 0 || o->help != NULL ; o++ ){ - if ( (strlen(txt) == 2 && txt[0] == '-' && o->shortVar == txt[1]) || (strlen(txt) > 2 && txt[0] == '-' && txt[1] == '-' && o->longVar != NULL && strcmp(txt + 2, o->longVar) == 0)){ - foundOption = 1; + if( o->shortVar == 0 && o->longVar == 0 ){ + // section + continue; + } - if( o->shortVar == 0 && o->longVar == 0 && o->help != NULL){ - // section - continue; - } + if ( (txt[0] == '-' && o->shortVar == txt[1]) || (strlen(txt) > 2 && txt[0] == '-' && txt[1] == '-' && o->longVar != NULL && strcmp(txt + 2, o->longVar) == 0)){ + foundOption = 1; // now process the option. switch(o->arg){ @@ -254,9 +297,13 @@ int option_parse(int argc, char ** argv, option_help * args, int * printhelp){ case (OPTION_REQUIRED_ARGUMENT):{ // check if next is an argument if(arg == NULL){ - // simply take the next value as argument - i++; - arg = argv[i]; + if(o->shortVar == txt[1] && txt[2] != 0){ + arg = & txt[2]; + }else{ + // simply take the next value as argument + i++; + arg = argv[i]; + } } switch(o->type){ @@ -269,7 +316,7 @@ int option_parse(int argc, char ** argv, option_help * args, int * printhelp){ break; } case('d'):{ - *(int*) o->variable = atoi(arg); + *(int*) o->variable = string_to_bytes(arg); break; } case('H'): @@ -285,7 +332,7 @@ int option_parse(int argc, char ** argv, option_help * args, int * printhelp){ break; } case('l'):{ - *(long long*) o->variable = atoll(arg); + *(long long*) o->variable = string_to_bytes(arg); break; } } diff --git a/src/option.h b/src/option.h index e2f5249..8337d8d 100644 --- a/src/option.h +++ b/src/option.h @@ -1,5 +1,7 @@ -#ifndef IOR_OPTION_H -#define IOR_OPTION_H +#ifndef _IOR_OPTION_H +#define _IOR_OPTION_H + +#include /* * Initial revision by JK @@ -23,6 +25,7 @@ typedef struct{ #define LAST_OPTION {0, 0, 0, (option_value_type) 0, 0, NULL} +int64_t string_to_bytes(char *size_str); void option_print_help(option_help * args, int is_plugin); void option_print_current(option_help * args); diff --git a/src/parse_options.c b/src/parse_options.c index 4696727..cc68263 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -22,54 +22,17 @@ #include -#include #include "utilities.h" #include "ior.h" #include "aiori.h" #include "parse_options.h" +#include "option.h" +#include "aiori.h" #define ISPOWEROFTWO(x) ((x != 0) && !(x & (x - 1))) IOR_param_t initialTestParams; -/* - * Takes a string of the form 64, 8m, 128k, 4g, etc. and converts to bytes. - */ -static IOR_offset_t StringToBytes(char *size_str) -{ - IOR_offset_t size = 0; - char range; - int rc; - - rc = sscanf(size_str, " %lld %c ", &size, &range); - if (rc == 2) { - switch ((int)range) { - case 'k': - case 'K': - size <<= 10; - break; - case 'm': - case 'M': - size <<= 20; - break; - case 'g': - case 'G': - size <<= 30; - break; - case 't': - case 'T': - size <<= 40; - break; - case 'p': - case 'P': - size <<= 50; - break; - } - } else if (rc == 0) { - size = -1; - } - return (size); -} static size_t NodeMemoryStringToBytes(char *size_str) { @@ -81,7 +44,7 @@ static size_t NodeMemoryStringToBytes(char *size_str) rc = sscanf(size_str, " %d %% ", &percent); if (rc == 0) - return (size_t)StringToBytes(size_str); + return (size_t) string_to_bytes(size_str); if (percent > 100 || percent < 0) ERR("percentage must be between 0 and 100"); @@ -98,11 +61,6 @@ static size_t NodeMemoryStringToBytes(char *size_str) return mem / 100 * percent; } -static void RecalculateExpectedFileSize(IOR_param_t *params) -{ - params->expectedAggFileSize = - params->blockSize * params->segmentCount * params->numTasks; -} /* * Check and correct all settings of each test in queue for correctness. @@ -143,13 +101,6 @@ static void CheckRunSettings(IOR_test_t *tests) params->openFlags |= IOR_WRONLY; } - /* If numTasks set to 0, use all tasks */ - if (params->numTasks == 0) { - MPI_CHECK(MPI_Comm_size(mpi_comm_world, - ¶ms->numTasks), - "MPI_Comm_size() error"); - RecalculateExpectedFileSize(params); - } } } @@ -247,15 +198,13 @@ void DecodeDirective(char *line, IOR_param_t *params) } else if (strcasecmp(option, "quitonerror") == 0) { params->quitOnError = atoi(value); } else if (strcasecmp(option, "segmentcount") == 0) { - params->segmentCount = StringToBytes(value); - RecalculateExpectedFileSize(params); + params->segmentCount = string_to_bytes(value); } else if (strcasecmp(option, "blocksize") == 0) { - params->blockSize = StringToBytes(value); - RecalculateExpectedFileSize(params); + params->blockSize = string_to_bytes(value); } else if (strcasecmp(option, "transfersize") == 0) { - params->transferSize = StringToBytes(value); + params->transferSize = string_to_bytes(value); } else if (strcasecmp(option, "setalignment") == 0) { - params->setAlignment = StringToBytes(value); + params->setAlignment = string_to_bytes(value); } else if (strcasecmp(option, "singlexferattempt") == 0) { params->singleXferAttempt = atoi(value); } else if (strcasecmp(option, "individualdatasets") == 0) { @@ -284,8 +233,6 @@ void DecodeDirective(char *line, IOR_param_t *params) params->useStridedDatatype = atoi(value); } else if (strcasecmp(option, "showhints") == 0) { params->showHints = atoi(value); - } else if (strcasecmp(option, "showhelp") == 0) { - params->showHelp = atoi(value); } else if (strcasecmp(option, "uniqueDir") == 0) { params->uniqueDir = atoi(value); } else if (strcasecmp(option, "useexistingtestfile") == 0) { @@ -297,7 +244,7 @@ void DecodeDirective(char *line, IOR_param_t *params) } else if (strcasecmp(option, "randomoffset") == 0) { params->randomOffset = atoi(value); } else if (strcasecmp(option, "memoryPerTask") == 0) { - params->memoryPerTask = StringToBytes(value); + params->memoryPerTask = string_to_bytes(value); params->memoryPerNode = 0; } else if (strcasecmp(option, "memoryPerNode") == 0) { params->memoryPerNode = NodeMemoryStringToBytes(value); @@ -312,7 +259,7 @@ void DecodeDirective(char *line, IOR_param_t *params) #ifndef HAVE_LUSTRE_LUSTRE_USER_H ERR("ior was not compiled with Lustre support"); #endif - params->lustre_stripe_size = StringToBytes(value); + params->lustre_stripe_size = string_to_bytes(value); params->lustre_set_striping = 1; } else if (strcasecmp(option, "lustrestartost") == 0) { #ifndef HAVE_LUSTRE_LUSTRE_USER_H @@ -346,12 +293,11 @@ void DecodeDirective(char *line, IOR_param_t *params) #ifndef HAVE_BEEGFS_BEEGFS_H ERR("ior was not compiled with BeeGFS support"); #endif - params->beegfs_chunkSize = StringToBytes(value); + params->beegfs_chunkSize = string_to_bytes(value); if (!ISPOWEROFTWO(params->beegfs_chunkSize) || params->beegfs_chunkSize < (1<<16)) ERR("beegfsChunkSize must be a power of two and >64k"); } else if (strcasecmp(option, "numtasks") == 0) { params->numTasks = atoi(value); - RecalculateExpectedFileSize(params); } else if (strcasecmp(option, "summaryalways") == 0) { params->summary_every_test = atoi(value); } else { @@ -377,7 +323,6 @@ void ParseLine(char *line, IOR_param_t * test) DecodeDirective(start, test); start = end + 1; } while (end != NULL); - } /* @@ -445,7 +390,6 @@ IOR_test_t *ReadConfigScript(char *scriptName) if (sscanf(linebuf, " #%s", empty) == 1) continue; if (contains_only(linebuf, "ior stop")) { - AllocResults(tail); break; } else if (contains_only(linebuf, "run")) { if (runflag) { @@ -454,7 +398,6 @@ IOR_test_t *ReadConfigScript(char *scriptName) tail->next = CreateTest(&tail->params, test_num++); tail = tail->next; } - AllocResults(tail); runflag = 1; } else if (runflag) { /* If this directive was preceded by a "run" line, then @@ -475,214 +418,148 @@ IOR_test_t *ReadConfigScript(char *scriptName) return head; } + +static IOR_param_t * parameters; + +static void decodeDirectiveWrapper(char *line){ + DecodeDirective(line, parameters); +} + /* * Parse Commandline. */ IOR_test_t *ParseCommandLine(int argc, char **argv) { - char * const opts = - "a:A:b:BcCd:D:eEf:FgG:hHi:Ij:J:kKl:mM:nN:o:O:pPqQ:rRs:St:T:uU:vVwWxX:YzZ"; + char * testscripts = NULL; + int toggleG = FALSE; + char * buffer_type = ""; + char * memoryPerNode = NULL; + init_IOR_Param_t(& initialTestParams); + parameters = & initialTestParams; + + option_help options [] = { + {'a', NULL, "API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI]", OPTION_OPTIONAL_ARGUMENT, 's', & initialTestParams.api}, + {'A', NULL, "refNum -- user supplied reference number to include in the summary", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.referenceNumber}, + {'b', NULL, "blockSize -- contiguous bytes to write per task (e.g.: 8, 4k, 2m, 1g)", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.blockSize}, + {'B', NULL, "useO_DIRECT -- uses O_DIRECT for POSIX, bypassing I/O buffers", OPTION_FLAG, 'd', & initialTestParams.useO_DIRECT}, + {'c', NULL, "collective -- collective I/O", OPTION_FLAG, 'd', & initialTestParams.collective}, + {'C', NULL, "reorderTasks -- changes task ordering to n+1 ordering for readback", OPTION_FLAG, 'd', & initialTestParams.reorderTasks}, + {'d', NULL, "interTestDelay -- delay between reps in seconds", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.interTestDelay}, + {'D', NULL, "deadlineForStonewalling -- seconds before stopping write or read phase", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.deadlineForStonewalling}, + {.help=" -O stoneWallingWearOut=1 -- once the stonewalling timout is over, all process finish to access the amount of data", .arg = OPTION_OPTIONAL_ARGUMENT}, + {.help=" -O stoneWallingWearOutIterations=N -- stop after processing this number of iterations, needed for reading data back written with stoneWallingWearOut", .arg = OPTION_OPTIONAL_ARGUMENT}, + {.help=" -O stoneWallingStatusFile=FILE -- this file keeps the number of iterations from stonewalling during write and allows to use them for read", .arg = OPTION_OPTIONAL_ARGUMENT}, + {'e', NULL, "fsync -- perform fsync/msync upon POSIX/MMAP write close", OPTION_FLAG, 'd', & initialTestParams.fsync}, + {'E', NULL, "useExistingTestFile -- do not remove test file before write access", OPTION_FLAG, 'd', & initialTestParams.useExistingTestFile}, + {'f', NULL, "scriptFile -- test script name", OPTION_FLAG, 'd', & testscripts}, + {'F', NULL, "filePerProc -- file-per-process", OPTION_FLAG, 'd', & initialTestParams.filePerProc}, + {'g', NULL, "intraTestBarriers -- use barriers between open, write/read, and close", OPTION_FLAG, 'd', & initialTestParams.intraTestBarriers}, + /* This option toggles between Incompressible Seed and Time stamp sig based on -l, + * so we'll toss the value in both for now, and sort it out in initialization + * after all the arguments are in and we know which it keep. + */ + {'G', NULL, "setTimeStampSignature -- set value for time stamp signature/random seed", OPTION_FLAG, 'd', & toggleG}, + {'H', NULL, "showHints -- show hints", OPTION_FLAG, 'd', & initialTestParams.showHints}, + {'i', NULL, "repetitions -- number of repetitions of test", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.repetitions}, + {'I', NULL, "individualDataSets -- datasets not shared by all procs [not working]", OPTION_FLAG, 'd', & initialTestParams.individualDataSets}, + {'j', NULL, "outlierThreshold -- warn on outlier N seconds from mean", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.outlierThreshold}, + {'J', NULL, "setAlignment -- HDF5 alignment in bytes (e.g.: 8, 4k, 2m, 1g)", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.setAlignment}, + {'k', NULL, "keepFile -- don't remove the test file(s) on program exit", OPTION_FLAG, 'd', & initialTestParams.keepFile}, + {'K', NULL, "keepFileWithError -- keep error-filled file(s) after data-checking", OPTION_FLAG, 'd', & initialTestParams.keepFileWithError}, + {'l', NULL, "datapacket type-- type of packet that will be created [offset|incompressible|timestamp|o|i|t]", OPTION_OPTIONAL_ARGUMENT, 's', & buffer_type}, + {'m', NULL, "multiFile -- use number of reps (-i) for multiple file count", OPTION_FLAG, 'd', & initialTestParams.multiFile}, + {'M', NULL, "memoryPerNode -- hog memory on the node (e.g.: 2g, 75%)", OPTION_OPTIONAL_ARGUMENT, 's', & memoryPerNode}, + {'n', NULL, "noFill -- no fill in HDF5 file creation", OPTION_FLAG, 'd', & initialTestParams.noFill}, + {'N', NULL, "numTasks -- number of tasks that should participate in the test", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.numTasks}, + {'o', NULL, "testFile -- full name for test", OPTION_OPTIONAL_ARGUMENT, 's', & initialTestParams.testFileName}, + {'O', NULL, "string of IOR directives (e.g. -O checkRead=1,lustreStripeCount=32)", OPTION_OPTIONAL_ARGUMENT, 'p', & decodeDirectiveWrapper}, + {'p', NULL, "preallocate -- preallocate file size", OPTION_FLAG, 'd', & initialTestParams.preallocate}, + {'P', NULL, "useSharedFilePointer -- use shared file pointer [not working]", OPTION_FLAG, 'd', & initialTestParams.useSharedFilePointer}, + {'q', NULL, "quitOnError -- during file error-checking, abort on error", OPTION_FLAG, 'd', & initialTestParams.quitOnError}, + {'Q', NULL, "taskPerNodeOffset for read tests use with -C & -Z options (-C constant N, -Z at least N)", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.taskPerNodeOffset}, + {'r', NULL, "readFile -- read existing file", OPTION_FLAG, 'd', & initialTestParams.readFile}, + {'R', NULL, "checkRead -- verify that the output of read matches the expected signature (used with -G)", OPTION_FLAG, 'd', & initialTestParams.checkRead}, + {'s', NULL, "segmentCount -- number of segments", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.segmentCount}, + {'S', NULL, "useStridedDatatype -- put strided access into datatype [not working]", OPTION_FLAG, 'd', & initialTestParams.useStridedDatatype}, + {'t', NULL, "transferSize -- size of transfer in bytes (e.g.: 8, 4k, 2m, 1g)", OPTION_OPTIONAL_ARGUMENT, 'l', & initialTestParams.transferSize}, + {'T', NULL, "maxTimeDuration -- max time in minutes executing repeated test; it aborts only between iterations and not within a test!", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.maxTimeDuration}, + {'u', NULL, "uniqueDir -- use unique directory name for each file-per-process", OPTION_FLAG, 'd', & initialTestParams.uniqueDir}, + {'U', NULL, "hintsFileName -- full name for hints file", OPTION_OPTIONAL_ARGUMENT, 's', & initialTestParams.hintsFileName}, + {'v', NULL, "verbose -- output information (repeating flag increases level)", OPTION_FLAG, 'd', & initialTestParams.verbose}, + {'V', NULL, "useFileView -- use MPI_File_set_view", OPTION_FLAG, 'd', & initialTestParams.useFileView}, + {'w', NULL, "writeFile -- write file", OPTION_FLAG, 'd', & initialTestParams.writeFile}, + {'W', NULL, "checkWrite -- check read after write", OPTION_FLAG, 'd', & initialTestParams.checkWrite}, + {'x', NULL, "singleXferAttempt -- do not retry transfer if incomplete", OPTION_FLAG, 'd', & initialTestParams.singleXferAttempt}, + {'X', NULL, "reorderTasksRandomSeed -- random seed for -Z option", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.reorderTasksRandomSeed}, + {'Y', NULL, "fsyncPerWrite -- perform fsync/msync after each POSIX/MMAP write", OPTION_FLAG, 'd', & initialTestParams.fsyncPerWrite}, + {'z', NULL, "randomOffset -- access is to random, not sequential, offsets within a file", OPTION_FLAG, 'd', & initialTestParams.randomOffset}, + {'Z', NULL, "reorderTasksRandom -- changes task ordering to random ordering for readback", OPTION_FLAG, 'd', & initialTestParams.reorderTasksRandom}, + {.help=" -O summaryFile=FILE -- store result data into this file", .arg = OPTION_OPTIONAL_ARGUMENT}, + {.help=" -O summaryFormat=[default,JSON,CSV] -- use the format for outputing the summary", .arg = OPTION_OPTIONAL_ARGUMENT}, + LAST_OPTION, + }; + int i; IOR_test_t *tests = NULL; - char * optarg; - init_IOR_Param_t(&initialTestParams); GetPlatformName(initialTestParams.platform); - initialTestParams.writeFile = initialTestParams.readFile = FALSE; - initialTestParams.checkWrite = initialTestParams.checkRead = FALSE; + int printhelp = 0; + int parsed_options = option_parse(argc, argv, options, & printhelp); - option_t *optList, *thisOpt; - optList = GetOptList(argc, argv, opts); - - while (optList != NULL) { - thisOpt = optList; - optarg = thisOpt->argument; - optList = optList->next; - switch (thisOpt->option) { - case 'a': - strcpy(initialTestParams.api, optarg); - break; - case 'A': - initialTestParams.referenceNumber = atoi(optarg); - break; - case 'b': - initialTestParams.blockSize = StringToBytes(optarg); - RecalculateExpectedFileSize(&initialTestParams); - break; - case 'B': - initialTestParams.useO_DIRECT = TRUE; - break; - case 'c': - initialTestParams.collective = TRUE; - break; - case 'C': - initialTestParams.reorderTasks = TRUE; - break; - case 'd': - initialTestParams.interTestDelay = atoi(optarg); - break; - case 'D': - initialTestParams.deadlineForStonewalling = - atoi(optarg); - break; - case 'e': - initialTestParams.fsync = TRUE; - break; - case 'E': - initialTestParams.useExistingTestFile = TRUE; - break; - case 'f': - tests = ReadConfigScript(optarg); - break; - case 'F': - initialTestParams.filePerProc = TRUE; - break; - case 'g': - initialTestParams.intraTestBarriers = TRUE; - break; - case 'G': - /* This option toggles between Incompressible Seed and Time stamp sig based on -l, - * so we'll toss the value in both for now, and sort it out in initialization - * after all the arguments are in and we know which it keep. - */ - initialTestParams.setTimeStampSignature = atoi(optarg); - initialTestParams.incompressibleSeed = atoi(optarg); - break; - case 'h': - initialTestParams.showHelp = TRUE; - break; - case 'H': - initialTestParams.showHints = TRUE; - break; - case 'i': - initialTestParams.repetitions = atoi(optarg); - break; - case 'I': - initialTestParams.individualDataSets = TRUE; - break; - case 'j': - initialTestParams.outlierThreshold = atoi(optarg); - break; - case 'J': - initialTestParams.setAlignment = StringToBytes(optarg); - break; - case 'k': - initialTestParams.keepFile = TRUE; - break; - case 'K': - initialTestParams.keepFileWithError = TRUE; - break; - case 'l': - switch(*optarg) { - case 'i': /* Incompressible */ - initialTestParams.dataPacketType = incompressible; - break; - case 't': /* timestamp */ - initialTestParams.dataPacketType = timestamp; - break; - case 'o': /* offset packet */ - initialTestParams.storeFileOffset = TRUE; - initialTestParams.dataPacketType = offset; - break; - default: - fprintf(out_logfile, - "Unknown arguement for -l %s generic assumed\n", optarg); - break; - } - break; - case 'm': - initialTestParams.multiFile = TRUE; - break; - case 'M': - initialTestParams.memoryPerNode = - NodeMemoryStringToBytes(optarg); - break; - case 'n': - initialTestParams.noFill = TRUE; - break; - case 'N': - initialTestParams.numTasks = atoi(optarg); - RecalculateExpectedFileSize(&initialTestParams); - break; - case 'o': - strcpy(initialTestParams.testFileName, optarg); - break; - case 'O': - ParseLine(optarg, &initialTestParams); - break; - case 'p': - initialTestParams.preallocate = TRUE; - break; - case 'P': - initialTestParams.useSharedFilePointer = TRUE; - break; - case 'q': - initialTestParams.quitOnError = TRUE; - break; - case 'Q': - initialTestParams.taskPerNodeOffset = atoi(optarg); - break; - case 'r': - initialTestParams.readFile = TRUE; - break; - case 'R': - initialTestParams.checkRead = TRUE; - break; - case 's': - initialTestParams.segmentCount = atoi(optarg); - RecalculateExpectedFileSize(&initialTestParams); - break; - case 'S': - initialTestParams.useStridedDatatype = TRUE; - break; - case 't': - initialTestParams.transferSize = StringToBytes(optarg); - break; - case 'T': - initialTestParams.maxTimeDuration = atoi(optarg); - break; - case 'u': - initialTestParams.uniqueDir = TRUE; - break; - case 'U': - strcpy(initialTestParams.hintsFileName, optarg); - break; - case 'v': - initialTestParams.verbose++; - break; - case 'V': - initialTestParams.useFileView = TRUE; - break; - case 'w': - initialTestParams.writeFile = TRUE; - break; - case 'W': - initialTestParams.checkWrite = TRUE; - break; - case 'x': - initialTestParams.singleXferAttempt = TRUE; - break; - case 'X': - initialTestParams.reorderTasksRandomSeed = atoi(optarg); - break; - case 'Y': - initialTestParams.fsyncPerWrite = TRUE; - break; - case 'z': - initialTestParams.randomOffset = TRUE; - break; - case 'Z': - initialTestParams.reorderTasksRandom = TRUE; - break; - default: - fprintf(out_logfile, - "ParseCommandLine: unknown option `-%c'.\n", - optopt); - } + if (toggleG){ + initialTestParams.setTimeStampSignature = toggleG; + initialTestParams.incompressibleSeed = toggleG; } - tests = CreateTest(&initialTestParams, 0); - AllocResults(tests); + if (buffer_type[0] != 0){ + switch(buffer_type[0]) { + case 'i': /* Incompressible */ + initialTestParams.dataPacketType = incompressible; + break; + case 't': /* timestamp */ + initialTestParams.dataPacketType = timestamp; + break; + case 'o': /* offset packet */ + initialTestParams.storeFileOffset = TRUE; + initialTestParams.dataPacketType = offset; + break; + default: + fprintf(out_logfile, + "Unknown arguement for -l %s; generic assumed\n", buffer_type); + break; + } + } + if (memoryPerNode){ + initialTestParams.memoryPerNode = NodeMemoryStringToBytes(optarg); + } + + /* get the version of the tests */ + + const ior_aiori_t * backend = aiori_select(initialTestParams.api); + initialTestParams.backend = backend; + initialTestParams.apiVersion = backend->get_version(); + + if(printhelp != 0){ + printf("Usage: %s ", argv[0]); + + option_print_help(options, 0); + + if(backend->get_options != NULL){ + printf("\nPlugin options for backend %s (%s)\n", initialTestParams.api, backend->get_version()); + option_print_help(backend->get_options(), 1); + } + if(printhelp == 1){ + exit(0); + }else{ + exit(1); + } + } + + if (testscripts){ + tests = ReadConfigScript(testscripts); + }else{ + tests = CreateTest(&initialTestParams, 0); + } CheckRunSettings(tests); From 5e98fa04084b34a423f45c7c7a30f3aba2fb8b55 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 14 Jul 2018 08:50:27 +0100 Subject: [PATCH 53/80] Dummy: option for xfer delay. --- src/aiori-DUMMY.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/aiori-DUMMY.c b/src/aiori-DUMMY.c index 660a16b..95ca76c 100755 --- a/src/aiori-DUMMY.c +++ b/src/aiori-DUMMY.c @@ -18,16 +18,19 @@ /************************** O P T I O N S *****************************/ struct dummy_options{ uint64_t delay_creates; + uint64_t delay_xfer; int delay_rank_0_only; }; static struct dummy_options o = { .delay_creates = 0, + .delay_xfer = 0, .delay_rank_0_only = 0, }; static option_help options [] = { {'c', "delay-create", "Delay per create in usec", OPTION_OPTIONAL_ARGUMENT, 'l', & o.delay_creates}, + {'x', "delay-xfer", "Delay per xfer in usec", OPTION_OPTIONAL_ARGUMENT, 'l', & o.delay_xfer}, {'z', "delay-only-rank0", "Delay only Rank0", OPTION_FLAG, 'd', & o.delay_rank_0_only}, LAST_OPTION }; @@ -97,8 +100,10 @@ static IOR_offset_t DUMMY_Xfer(int access, void *file, IOR_size_t * buffer, IOR_ if(verbose > 4){ fprintf(out_logfile, "DUMMY xfer: %p\n", file); } - if (rank == 0){ - usleep(100000); + if (o.delay_xfer){ + if (! o.delay_rank_0_only || (o.delay_rank_0_only && rank == 0)){ + usleep(o.delay_xfer); + } } return length; } From a0b8659cc82c38efcbd2997e8ef0bae2bc27ad90 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 14 Jul 2018 09:05:23 +0100 Subject: [PATCH 54/80] IOR appears to work with the new option parser. --- src/ior-output.c | 2 +- src/option.c | 8 ++++++++ src/parse_options.c | 22 ++++++++++++---------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/ior-output.c b/src/ior-output.c index 0bb3d5a..10a9a86 100644 --- a/src/ior-output.c +++ b/src/ior-output.c @@ -78,7 +78,7 @@ static void PrintKeyValEnd(){ } static void PrintKeyVal(char * key, char * value){ - if(value[0] != 0 && value[strlen(value) -1 ] == '\n'){ + if(value != NULL && value[0] != 0 && value[strlen(value) -1 ] == '\n'){ // remove \n value[strlen(value) -1 ] = 0; } diff --git a/src/option.c b/src/option.c index 13b239d..13e1908 100644 --- a/src/option.c +++ b/src/option.c @@ -307,6 +307,12 @@ int option_parse(int argc, char ** argv, option_help * args, int * printhelp){ } switch(o->type){ + case('p'):{ + // call the function in the variable + void(*fp)() = o->variable; + fp(arg); + break; + } case('F'):{ *(double*) o->variable = atof(arg); break; @@ -335,6 +341,8 @@ int option_parse(int argc, char ** argv, option_help * args, int * printhelp){ *(long long*) o->variable = string_to_bytes(arg); break; } + default: + printf("ERROR: Unknown option type %c\n", o->type); } } } diff --git a/src/parse_options.c b/src/parse_options.c index cc68263..e73c2e6 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -120,7 +120,7 @@ void DecodeDirective(char *line, IOR_param_t *params) MPI_CHECK(MPI_Abort(MPI_COMM_WORLD, -1), "MPI_Abort() error"); } if (strcasecmp(option, "api") == 0) { - strcpy(params->api, value); + params->api = strdup(value); } else if (strcasecmp(option, "summaryFile") == 0) { if (rank == 0){ out_resultfile = fopen(value, "w"); @@ -142,13 +142,13 @@ void DecodeDirective(char *line, IOR_param_t *params) } else if (strcasecmp(option, "refnum") == 0) { params->referenceNumber = atoi(value); } else if (strcasecmp(option, "debug") == 0) { - strcpy(params->debug, value); + params->debug = strdup(value); } else if (strcasecmp(option, "platform") == 0) { - strcpy(params->platform, value); + params->platform = strdup(value); } else if (strcasecmp(option, "testfile") == 0) { - strcpy(params->testFileName, value); + params->testFileName = strdup(value); } else if (strcasecmp(option, "hintsfilename") == 0) { - strcpy(params->hintsFileName, value); + params->hintsFileName = strdup(value); } else if (strcasecmp(option, "deadlineforstonewalling") == 0) { params->deadlineForStonewalling = atoi(value); } else if (strcasecmp(option, "stoneWallingWearOut") == 0) { @@ -156,7 +156,7 @@ void DecodeDirective(char *line, IOR_param_t *params) } else if (strcasecmp(option, "stoneWallingWearOutIterations") == 0) { params->stoneWallingWearOutIterations = atoll(value); } else if (strcasecmp(option, "stoneWallingStatusFile") == 0) { - strcpy(params->stoneWallingStatusFile, value); + params->stoneWallingStatusFile = strdup(value); } else if (strcasecmp(option, "maxtimeduration") == 0) { params->maxTimeDuration = atoi(value); } else if (strcasecmp(option, "outlierthreshold") == 0) { @@ -451,7 +451,7 @@ IOR_test_t *ParseCommandLine(int argc, char **argv) {.help=" -O stoneWallingStatusFile=FILE -- this file keeps the number of iterations from stonewalling during write and allows to use them for read", .arg = OPTION_OPTIONAL_ARGUMENT}, {'e', NULL, "fsync -- perform fsync/msync upon POSIX/MMAP write close", OPTION_FLAG, 'd', & initialTestParams.fsync}, {'E', NULL, "useExistingTestFile -- do not remove test file before write access", OPTION_FLAG, 'd', & initialTestParams.useExistingTestFile}, - {'f', NULL, "scriptFile -- test script name", OPTION_FLAG, 'd', & testscripts}, + {'f', NULL, "scriptFile -- test script name", OPTION_OPTIONAL_ARGUMENT, 's', & testscripts}, {'F', NULL, "filePerProc -- file-per-process", OPTION_FLAG, 'd', & initialTestParams.filePerProc}, {'g', NULL, "intraTestBarriers -- use barriers between open, write/read, and close", OPTION_FLAG, 'd', & initialTestParams.intraTestBarriers}, /* This option toggles between Incompressible Seed and Time stamp sig based on -l, @@ -532,13 +532,15 @@ IOR_test_t *ParseCommandLine(int argc, char **argv) if (memoryPerNode){ initialTestParams.memoryPerNode = NodeMemoryStringToBytes(optarg); } - - /* get the version of the tests */ - + const ior_aiori_t * backend = aiori_select(initialTestParams.api); initialTestParams.backend = backend; initialTestParams.apiVersion = backend->get_version(); + if(backend->get_options != NULL){ + option_parse(argc - parsed_options, argv + parsed_options, backend->get_options(), & printhelp); + } + if(printhelp != 0){ printf("Usage: %s ", argv[0]); From 5ef95675bf2c15fa8ba92747a66957d39506cde7 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 14 Jul 2018 09:29:08 +0100 Subject: [PATCH 55/80] AIORI initialize() and finalize() functions. --- src/aiori-DUMMY.c | 2 ++ src/aiori-S3.c | 20 ++++++++++++++++++++ src/aiori.c | 22 ++++++++++++++++++++++ src/aiori.h | 4 ++++ src/ior.c | 27 +++------------------------ src/mdtest-main.c | 3 +++ 6 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/aiori-DUMMY.c b/src/aiori-DUMMY.c index 95ca76c..8e129c7 100755 --- a/src/aiori-DUMMY.c +++ b/src/aiori-DUMMY.c @@ -149,5 +149,7 @@ ior_aiori_t dummy_aiori = { DUMMY_rmdir, DUMMY_access, DUMMY_stat, + NULL, + NULL, DUMMY_options }; diff --git a/src/aiori-S3.c b/src/aiori-S3.c index 7e45711..955e0f0 100755 --- a/src/aiori-S3.c +++ b/src/aiori-S3.c @@ -124,6 +124,8 @@ static void EMC_Close(void*, IOR_param_t*); static void S3_Delete(char*, IOR_param_t*); static void S3_Fsync(void*, IOR_param_t*); static IOR_offset_t S3_GetFileSize(IOR_param_t*, MPI_Comm, char*); +static void S3_init(); +static void S3_finalize(); /************************** D E C L A R A T I O N S ***************************/ @@ -140,6 +142,8 @@ ior_aiori_t s3_aiori = { .get_version = aiori_get_version, .fsync = S3_Fsync, .get_file_size = S3_GetFileSize, + .initialize = S3_init, + .finalize = S3_finalize }; // "S3", plus EMC-extensions enabled @@ -155,6 +159,8 @@ ior_aiori_t s3_plus_aiori = { .set_version = S3_SetVersion, .fsync = S3_Fsync, .get_file_size = S3_GetFileSize, + .initialize = S3_init, + .finalize = S3_finalize }; // Use EMC-extensions for N:1 write, as well @@ -170,8 +176,22 @@ ior_aiori_t s3_emc_aiori = { .set_version = S3_SetVersion, .fsync = S3_Fsync, .get_file_size = S3_GetFileSize, + .initialize = S3_init, + .finalize = S3_finalize }; +static void S3_init(){ + /* This is supposed to be done before *any* threads are created. + * Could MPI_Init() create threads (or call multi-threaded + * libraries)? We'll assume so. */ + AWS4C_CHECK( aws_init() ); +} + +static void S3_finalize(){ + /* done once per program, after exiting all threads. + * NOTE: This fn doesn't return a value that can be checked for success. */ + aws_cleanup(); +} /* modelled on similar macros in iordef.h */ #define CURL_ERR(MSG, CURL_ERRNO, PARAM) \ diff --git a/src/aiori.c b/src/aiori.c index 2816bc1..8ec7130 100644 --- a/src/aiori.c +++ b/src/aiori.c @@ -117,6 +117,28 @@ char* aiori_get_version() return ""; } +void aiori_initialize(){ + /* Sanity check, we were compiled with SOME backend, right? */ + if (0 == aiori_count ()) { + ERR("No IO backends compiled into aiori. " + "Run 'configure --with-', and recompile."); + } + + for (ior_aiori_t **tmp = available_aiori ; *tmp != NULL; ++tmp) { + if((*tmp)->initialize){ + (*tmp)->initialize(); + } + } +} + +void aiori_finalize(){ + for (ior_aiori_t **tmp = available_aiori ; *tmp != NULL; ++tmp) { + if((*tmp)->finalize){ + (*tmp)->finalize(); + } + } +} + const ior_aiori_t *aiori_select (const char *api) { char warn_str[256] = {0}; diff --git a/src/aiori.h b/src/aiori.h index afb763f..0046e1f 100755 --- a/src/aiori.h +++ b/src/aiori.h @@ -79,6 +79,8 @@ typedef struct ior_aiori { int (*rmdir) (const char *path, IOR_param_t * param); int (*access) (const char *path, int mode, IOR_param_t * param); int (*stat) (const char *path, struct stat *buf, IOR_param_t * param); + void (*initialize)(); /* called once per program before MPI is started */ + void (*finalize)(); /* called once per program after MPI is shutdown */ option_help * (*get_options)(); } ior_aiori_t; @@ -94,6 +96,8 @@ extern ior_aiori_t s3_plus_aiori; extern ior_aiori_t s3_emc_aiori; extern ior_aiori_t rados_aiori; +void aiori_initialize(); +void aiori_finalize(); const ior_aiori_t *aiori_select (const char *api); int aiori_count (void); const char *aiori_default (void); diff --git a/src/ior.c b/src/ior.c index 4cfd821..4307558 100755 --- a/src/ior.c +++ b/src/ior.c @@ -62,12 +62,6 @@ IOR_test_t * ior_run(int argc, char **argv, MPI_Comm world_com, FILE * world_out MPI_CHECK(MPI_Comm_rank(mpi_comm_world, &rank), "cannot get rank"); PrintEarlyHeader(); - /* Sanity check, we were compiled with SOME backend, right? */ - if (0 == aiori_count ()) { - ERR("No IO backends compiled into ior. " - "Run 'configure --with-', and recompile."); - } - /* setup tests, and validate parameters */ tests_head = ParseCommandLine(argc, argv); InitTests(tests_head, world_com); @@ -105,18 +99,13 @@ int ior_main(int argc, char **argv) out_logfile = stdout; out_resultfile = stdout; + aiori_initialize(); + /* * check -h option from commandline without starting MPI; */ tests_head = ParseCommandLine(argc, argv); - #ifdef USE_S3_AIORI - /* This is supposed to be done before *any* threads are created. - * Could MPI_Init() create threads (or call multi-threaded - * libraries)? We'll assume so. */ - AWS4C_CHECK( aws_init() ); - #endif - /* start the MPI code */ MPI_CHECK(MPI_Init(&argc, &argv), "cannot initialize MPI"); @@ -131,12 +120,6 @@ int ior_main(int argc, char **argv) /*MPI_CHECK(MPI_Errhandler_set(mpi_comm_world, MPI_ERRORS_RETURN), "cannot set errhandler"); */ - /* Sanity check, we were compiled with SOME backend, right? */ - if (0 == aiori_count ()) { - ERR("No IO backends compiled into ior. " - "Run 'configure --with-', and recompile."); - } - /* setup tests, and validate parameters */ InitTests(tests_head, mpi_comm_world); verbose = tests_head->params.verbose; @@ -173,11 +156,7 @@ int ior_main(int argc, char **argv) MPI_CHECK(MPI_Finalize(), "cannot finalize MPI"); - #ifdef USE_S3_AIORI - /* done once per program, after exiting all threads. - * NOTE: This fn doesn't return a value that can be checked for success. */ - aws_cleanup(); - #endif + aiori_finalize(); return totalErrorCount; } diff --git a/src/mdtest-main.c b/src/mdtest-main.c index 0230a77..854456f 100644 --- a/src/mdtest-main.c +++ b/src/mdtest-main.c @@ -1,10 +1,13 @@ #include "mdtest.h" +#include "aiori.h" int main(int argc, char **argv) { + aiori_initialize(); MPI_Init(&argc, &argv); mdtest_run(argc, argv, MPI_COMM_WORLD, stdout); MPI_Finalize(); + aiori_finalize(); return 0; } From ac7600d662536e7bf0aa3b0126acf789b786b2b2 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 14 Jul 2018 10:27:31 +0100 Subject: [PATCH 56/80] Unit-test with automake for library version. Minor fix for library version. --- README.md | 2 ++ configure.ac | 2 ++ src/Makefile.am | 7 +++++ src/aiori-S3.c | 72 +++++++++++++++++++++++++++++--------------- src/ior.c | 26 +--------------- src/ior.h | 16 ---------- src/option.c | 4 ++- src/test/Makefile.am | 7 +++++ src/test/lib.c | 20 ++++++++++++ 9 files changed, 90 insertions(+), 66 deletions(-) create mode 100644 src/test/Makefile.am create mode 100644 src/test/lib.c diff --git a/README.md b/README.md index dd9ee1e..81a1fa3 100755 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ See also NOTES.txt # Testing + Run "make check" to invoke the unit test framework of Automake. + * To run basic functionality tests that we use for continuous integration, see ./testing/ * There are docker scripts provided to test various distributions at once. * See ./testing/docker/ diff --git a/configure.ac b/configure.ac index a80908e..3a34720 100755 --- a/configure.ac +++ b/configure.ac @@ -26,6 +26,7 @@ AX_PROG_CC_MPI(,,[ AC_MSG_FAILURE([MPI compiler requested, but couldn't use MPI.]) ]) +AC_PROG_RANLIB # No reason not to require modern C at this point AC_PROG_CC_C99 @@ -244,5 +245,6 @@ AM_CONDITIONAL([USE_CAPS], [test x$enable_caps = xyes]) AC_CONFIG_FILES([Makefile src/Makefile contrib/Makefile + src/test/Makefile doc/Makefile]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am index 9c27d89..0f1c913 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,3 +1,5 @@ +SUBDIRS = . test + bin_PROGRAMS = ior mdtest if USE_CAPS bin_PROGRAMS += IOR MDTEST @@ -5,6 +7,9 @@ endif noinst_HEADERS = ior.h utilities.h parse_options.h aiori.h iordef.h ior-internal.h option.h +lib_LIBRARIES = libaiori.a +libaiori_a_SOURCES = ior.c mdtest.c utilities.c parse_options.c ior-output.c option.c + extraSOURCES = aiori.c aiori-DUMMY.c extraLDADD = extraLDFLAGS = @@ -88,3 +93,5 @@ IOT_CPPFLAGS = $(ior_CPPFLAGS) MDTEST_SOURCES = $(mdtest_SOURCES) MDTEST_LDADD = $(mdtest_LDADD) MDTEST_CPPFLAGS = $(mdtest_CPPFLAGS) + +libaiori_a_SOURCES += $(extraSOURCES) diff --git a/src/aiori-S3.c b/src/aiori-S3.c index 955e0f0..4836dd1 100755 --- a/src/aiori-S3.c +++ b/src/aiori-S3.c @@ -110,6 +110,39 @@ #include "aws4c_extra.h" // utilities, e.g. for parsing XML in responses + + +/* buffer is used to generate URLs, err_msgs, etc */ +#define BUFF_SIZE 1024 +static char buff[BUFF_SIZE]; + +const int ETAG_SIZE = 32; + +CURLcode rc; + +/* Any objects we create or delete will be under this bucket */ +const char* bucket_name = "ior"; + +/* TODO: The following stuff goes into options! */ +/* REST/S3 variables */ +// CURL* curl; /* for libcurl "easy" fns (now managed by aws4c) */ +# define IOR_CURL_INIT 0x01 /* curl top-level inits were perfomed once? */ +# define IOR_CURL_NOCONTINUE 0x02 +# define IOR_CURL_S3_EMC_EXT 0x04 /* allow EMC extensions to S3? */ + +#ifdef USE_S3_AIORI +# include +# include "aws4c.h" +#else + typedef void CURL; /* unused, but needs a type */ + typedef void IOBuf; /* unused, but needs a type */ +#endif + + IOBuf* io_buf; /* aws4c places parsed header values here */ + IOBuf* etags; /* accumulate ETags for N:1 parts */ + +/////////////////////////////////////////////// + /**************************** P R O T O T Y P E S *****************************/ static void* S3_Create(char*, IOR_param_t*); static void* S3_Open(char*, IOR_param_t*); @@ -180,17 +213,23 @@ ior_aiori_t s3_emc_aiori = { .finalize = S3_finalize }; +static int is_initialized = FALSE; + static void S3_init(){ - /* This is supposed to be done before *any* threads are created. - * Could MPI_Init() create threads (or call multi-threaded - * libraries)? We'll assume so. */ - AWS4C_CHECK( aws_init() ); + if (is_initialized) return; + is_initialized = TRUE; + /* This is supposed to be done before *any* threads are created. + * Could MPI_Init() create threads (or call multi-threaded + * libraries)? We'll assume so. */ + AWS4C_CHECK( aws_init() ); } static void S3_finalize(){ - /* done once per program, after exiting all threads. - * NOTE: This fn doesn't return a value that can be checked for success. */ - aws_cleanup(); + if (! is_initialized) return; + is_initialized = FALSE; + /* done once per program, after exiting all threads. + * NOTE: This fn doesn't return a value that can be checked for success. */ + aws_cleanup(); } /* modelled on similar macros in iordef.h */ @@ -213,20 +252,6 @@ static void S3_finalize(){ } while (0) - -/* buffer is used to generate URLs, err_msgs, etc */ -#define BUFF_SIZE 1024 -static char buff[BUFF_SIZE]; - -const int ETAG_SIZE = 32; - -CURLcode rc; - -/* Any objects we create or delete will be under this bucket */ -const char* bucket_name = "ior"; -//const char* bucket_name = "brettk"; - - /***************************** F U N C T I O N S ******************************/ @@ -251,9 +276,8 @@ const char* bucket_name = "ior"; * --------------------------------------------------------------------------- */ -static -void -s3_connect( IOR_param_t* param ) { + +static void s3_connect( IOR_param_t* param ) { if (param->verbose >= VERBOSE_2) { printf("-> s3_connect\n"); /* DEBUGGING */ } diff --git a/src/ior.c b/src/ior.c index 4307558..e493d09 100755 --- a/src/ior.c +++ b/src/ior.c @@ -210,11 +210,7 @@ void init_IOR_Param_t(IOR_param_t * p) p->hdfs_replicas = 0; /* invokes the default */ p->hdfs_block_size = 0; - // p->curl = NULL; p->URI = NULL; - p->curl_flags = 0; - p->io_buf = NULL; - p->etags = NULL; p->part_number = 0; p->beegfs_numTargets = -1; @@ -223,26 +219,6 @@ void init_IOR_Param_t(IOR_param_t * p) p->mmap_ptr = NULL; } -/** - * Bind the global "backend" pointer to the requested backend AIORI's - * function table. - */ -static void AioriBind(char* api, IOR_param_t* param) -{ - backend = aiori_select (api); - if (NULL != backend) { - if (! strncmp(api, "S3", 2)) { - if (! strcasecmp(api, "S3_EMC")) { - param->curl_flags |= IOR_CURL_S3_EMC_EXT; - } else { - param->curl_flags &= ~(IOR_CURL_S3_EMC_EXT); - } - } - } else { - ERR("unrecognized IO API"); - } -} - static void DisplayOutliers(int numTasks, double timerVal, @@ -1235,7 +1211,7 @@ static void TestIoSys(IOR_test_t *test) } /* bind I/O calls to specific API */ - AioriBind(params->api, params); + backend = aiori_select(params->api); /* show test setup */ if (rank == 0 && verbose >= VERBOSE_0) diff --git a/src/ior.h b/src/ior.h index b6655b3..24a6d53 100755 --- a/src/ior.h +++ b/src/ior.h @@ -29,14 +29,6 @@ typedef void* hdfsFS; /* unused, but needs a type */ #endif -#ifdef USE_S3_AIORI -# include -# include "aws4c.h" -#else - typedef void CURL; /* unused, but needs a type */ - typedef void IOBuf; /* unused, but needs a type */ -#endif - #ifdef USE_RADOS_AIORI # include #else @@ -180,15 +172,7 @@ typedef struct int hdfs_replicas; /* n block replicas. (0 gets default) */ int hdfs_block_size; /* internal blk-size. (0 gets default) */ - /* REST/S3 variables */ - // CURL* curl; /* for libcurl "easy" fns (now managed by aws4c) */ -# define IOR_CURL_INIT 0x01 /* curl top-level inits were perfomed once? */ -# define IOR_CURL_NOCONTINUE 0x02 -# define IOR_CURL_S3_EMC_EXT 0x04 /* allow EMC extensions to S3? */ - char curl_flags; char* URI; /* "path" to target object */ - IOBuf* io_buf; /* aws4c places parsed header values here */ - IOBuf* etags; /* accumulate ETags for N:1 parts */ size_t part_number; /* multi-part upload increment (PER-RANK!) */ char* UploadId; /* key for multi-part-uploads */ diff --git a/src/option.c b/src/option.c index 13e1908..831afb2 100644 --- a/src/option.c +++ b/src/option.c @@ -267,9 +267,11 @@ int option_parse(int argc, char ** argv, option_help * args, int * printhelp){ char * txt = argv[i]; int foundOption = 0; char * arg = strstr(txt, "="); + int replaced_equal = 0; if(arg != NULL){ arg[0] = 0; arg++; + replaced_equal = 1; } if(strcmp(txt, "--") == 0){ // we found plugin options @@ -346,7 +348,7 @@ int option_parse(int argc, char ** argv, option_help * args, int * printhelp){ } } } - if(arg != NULL){ + if(replaced_equal){ arg[-1] = '='; } diff --git a/src/test/Makefile.am b/src/test/Makefile.am new file mode 100644 index 0000000..751423f --- /dev/null +++ b/src/test/Makefile.am @@ -0,0 +1,7 @@ +TESTS = testlib +bin_PROGRAMS = testlib + +testlib_SOURCES = lib.c +testlib_LDFLAGS = +testlib_LDADD = ../libaiori.a +testlib_CPPFLAGS = diff --git a/src/test/lib.c b/src/test/lib.c new file mode 100644 index 0000000..64ebf31 --- /dev/null +++ b/src/test/lib.c @@ -0,0 +1,20 @@ +#include "../ior.h" +#include "../mdtest.h" + +int main(int argc, char ** argv){ + int rank; + MPI_Init(& argc, & argv); + MPI_Comm_rank(MPI_COMM_WORLD, & rank); + + if (rank == 0){ + char * param[] = {"./ior", "-a", "DUMMY"}; + IOR_test_t * res = ior_run(3, param, MPI_COMM_SELF, stdout); + } + if (rank == 0){ + char * param[] = {"./mdtest", "-a", "DUMMY"}; + mdtest_results_t * res = mdtest_run(3, param, MPI_COMM_SELF, stdout); + } + MPI_Finalize(); + + return 0; +} From b613bf9396c774578ccce05d4a227ea7885bd1ef Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 14 Jul 2018 10:47:25 +0100 Subject: [PATCH 57/80] AIORI update to initialization and fix HDF5 with changed API. --- src/aiori-HDF5.c | 17 ++++++++++------- src/aiori-S3.c | 5 ----- src/aiori.c | 8 ++++++++ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/aiori-HDF5.c b/src/aiori-HDF5.c index 5d629e1..5c0f139 100755 --- a/src/aiori-HDF5.c +++ b/src/aiori-HDF5.c @@ -89,7 +89,7 @@ static IOR_offset_t HDF5_Xfer(int, void *, IOR_size_t *, IOR_offset_t, IOR_param_t *); static void HDF5_Close(void *, IOR_param_t *); static void HDF5_Delete(char *, IOR_param_t *); -static void HDF5_SetVersion(IOR_param_t *); +static char* HDF5_GetVersion(); static void HDF5_Fsync(void *, IOR_param_t *); static IOR_offset_t HDF5_GetFileSize(IOR_param_t *, MPI_Comm, char *); static int HDF5_Access(const char *, int, IOR_param_t *); @@ -103,7 +103,7 @@ ior_aiori_t hdf5_aiori = { .xfer = HDF5_Xfer, .close = HDF5_Close, .delete = HDF5_Delete, - .set_version = HDF5_SetVersion, + .get_version = HDF5_GetVersion, .fsync = HDF5_Fsync, .get_file_size = HDF5_GetFileSize, .statfs = aiori_posix_statfs, @@ -447,20 +447,23 @@ static void HDF5_Delete(char *testFileName, IOR_param_t * param) /* * Determine api version. */ -static void HDF5_SetVersion(IOR_param_t * test) +static char * HDF5_GetVersion() { + static char version[1024] = {0}; + if(version[0]) return version; + unsigned major, minor, release; if (H5get_libversion(&major, &minor, &release) < 0) { WARN("cannot get HDF5 library version"); } else { - sprintf(test->apiVersion, "%s-%u.%u.%u", - test->api, major, minor, release); + sprintf(version, "%u.%u.%u", major, minor, release); } #ifndef H5_HAVE_PARALLEL - strcat(test->apiVersion, " (Serial)"); + strcat(version, " (Serial)"); #else /* H5_HAVE_PARALLEL */ - strcat(test->apiVersion, " (Parallel)"); + strcat(version, " (Parallel)"); #endif /* not H5_HAVE_PARALLEL */ + return version; } /* diff --git a/src/aiori-S3.c b/src/aiori-S3.c index 4836dd1..326b5bc 100755 --- a/src/aiori-S3.c +++ b/src/aiori-S3.c @@ -213,11 +213,8 @@ ior_aiori_t s3_emc_aiori = { .finalize = S3_finalize }; -static int is_initialized = FALSE; static void S3_init(){ - if (is_initialized) return; - is_initialized = TRUE; /* This is supposed to be done before *any* threads are created. * Could MPI_Init() create threads (or call multi-threaded * libraries)? We'll assume so. */ @@ -225,8 +222,6 @@ static void S3_init(){ } static void S3_finalize(){ - if (! is_initialized) return; - is_initialized = FALSE; /* done once per program, after exiting all threads. * NOTE: This fn doesn't return a value that can be checked for success. */ aws_cleanup(); diff --git a/src/aiori.c b/src/aiori.c index 8ec7130..b886487 100644 --- a/src/aiori.c +++ b/src/aiori.c @@ -117,7 +117,12 @@ char* aiori_get_version() return ""; } +static int is_initialized = FALSE; + void aiori_initialize(){ + if (is_initialized) return; + is_initialized = TRUE; + /* Sanity check, we were compiled with SOME backend, right? */ if (0 == aiori_count ()) { ERR("No IO backends compiled into aiori. " @@ -132,6 +137,9 @@ void aiori_initialize(){ } void aiori_finalize(){ + if (! is_initialized) return; + is_initialized = FALSE; + for (ior_aiori_t **tmp = available_aiori ; *tmp != NULL; ++tmp) { if((*tmp)->finalize){ (*tmp)->finalize(); From 04ba846da2cb1a406a7c610168f30e81e03bfa11 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 14 Jul 2018 11:11:47 +0100 Subject: [PATCH 58/80] Fix for SetHints (after changing way strings are managed). --- src/Makefile.am | 23 +++++++++++++++++------ src/test/Makefile.am | 7 ------- src/utilities.c | 2 +- 3 files changed, 18 insertions(+), 14 deletions(-) delete mode 100644 src/test/Makefile.am diff --git a/src/Makefile.am b/src/Makefile.am index 0f1c913..94416cd 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = . test +SUBDIRS = . bin_PROGRAMS = ior mdtest if USE_CAPS @@ -9,20 +9,21 @@ noinst_HEADERS = ior.h utilities.h parse_options.h aiori.h iordef.h ior-internal lib_LIBRARIES = libaiori.a libaiori_a_SOURCES = ior.c mdtest.c utilities.c parse_options.c ior-output.c option.c +libaiori_a_LIBADD = extraSOURCES = aiori.c aiori-DUMMY.c extraLDADD = extraLDFLAGS = extraCPPFLAGS = -ior_SOURCES = ior-main.c ior.c utilities.c parse_options.c ior-output.c option.c +ior_SOURCES = ior-main.c ior_LDFLAGS = -ior_LDADD = +ior_LDADD = libaiori.a ior_CPPFLAGS = -mdtest_SOURCES = mdtest-main.c mdtest.c utilities.c option.c +mdtest_SOURCES = mdtest-main.c mdtest_LDFLAGS = -mdtest_LDADD = +mdtest_LDADD = libaiori.a mdtest_CPPFLAGS = if USE_HDFS_AIORI @@ -75,7 +76,6 @@ extraLDADD += -laws4c extraLDADD += -laws4c_extra endif - ior_SOURCES += $(extraSOURCES) ior_LDFLAGS += $(extraLDFLAGS) ior_LDADD += $(extraLDADD) @@ -95,3 +95,14 @@ MDTEST_LDADD = $(mdtest_LDADD) MDTEST_CPPFLAGS = $(mdtest_CPPFLAGS) libaiori_a_SOURCES += $(extraSOURCES) +#libaiori_a_LIBADD += $(extraLDFLAGS) $(extraLDADD) + + + +TESTS = testlib +bin_PROGRAMS += testlib + +testlib_SOURCES = ./test/lib.c +testlib_LDFLAGS = +testlib_LDADD = libaiori.a $(extraLDADD) +testlib_CPPFLAGS = diff --git a/src/test/Makefile.am b/src/test/Makefile.am deleted file mode 100644 index 751423f..0000000 --- a/src/test/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -TESTS = testlib -bin_PROGRAMS = testlib - -testlib_SOURCES = lib.c -testlib_LDFLAGS = -testlib_LDADD = ../libaiori.a -testlib_CPPFLAGS = diff --git a/src/utilities.c b/src/utilities.c index acc6a85..d0357a1 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -266,7 +266,7 @@ void SetHints(MPI_Info * mpiHints, char *hintsFileName) } /* get hints from hints file */ - if (strcmp(hintsFileName, "") != 0) { + if (hintsFileName != NULL && strcmp(hintsFileName, "") != 0) { /* open the hint file */ fd = fopen(hintsFileName, "r"); From bd253cf05894dabfd6ab220fcdc4b46ed004aace Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 14 Jul 2018 11:23:43 +0100 Subject: [PATCH 59/80] Added compilation test for HDF5. --- configure.ac | 1 - testing/docker/ubuntu14.04/Dockerfile | 3 ++- testing/docker/ubuntu14.04/run-test.sh | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 3a34720..7b49a39 100755 --- a/configure.ac +++ b/configure.ac @@ -245,6 +245,5 @@ AM_CONDITIONAL([USE_CAPS], [test x$enable_caps = xyes]) AC_CONFIG_FILES([Makefile src/Makefile contrib/Makefile - src/test/Makefile doc/Makefile]) AC_OUTPUT diff --git a/testing/docker/ubuntu14.04/Dockerfile b/testing/docker/ubuntu14.04/Dockerfile index 735178b..06f9326 100644 --- a/testing/docker/ubuntu14.04/Dockerfile +++ b/testing/docker/ubuntu14.04/Dockerfile @@ -3,4 +3,5 @@ FROM ubuntu:14.04 WORKDIR /data RUN apt-get update RUN apt-get install -y libopenmpi-dev openmpi-bin mpich git pkg-config gcc-4.7 nano make -RUN apt-get install -y sudo +RUN apt-get install -y sudo hdf5-tools libhdf5-mpi-dev + diff --git a/testing/docker/ubuntu14.04/run-test.sh b/testing/docker/ubuntu14.04/run-test.sh index b8d47c2..020cad0 100755 --- a/testing/docker/ubuntu14.04/run-test.sh +++ b/testing/docker/ubuntu14.04/run-test.sh @@ -14,8 +14,8 @@ function runTest(){ sudo -u testuser mkdir -p $BUILD/$FLAVOR pushd $BUILD/$FLAVOR > /dev/null - sudo -u testuser /data/configure || exit 1 - sudo -u testuser make || exit 1 + sudo -u testuser /data/configure --with-hdf5 CFLAGS=-I/usr/lib/x86_64-linux-gnu/hdf5/openmpi/include LDFLAGS=-L/usr/lib/x86_64-linux-gnu/hdf5/openmpi/lib|| exit 1 + sudo -u testuser make V=1 || exit 1 #define the alias ln -sf $(which mpiexec.$FLAVOR) /usr/bin/mpiexec From 5d4ac1a0a098996c08d8bcde0efadbfd8b23ec26 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 14 Jul 2018 11:32:39 +0100 Subject: [PATCH 60/80] Bugfix parser: wrong datatype --- src/parse_options.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parse_options.c b/src/parse_options.c index e73c2e6..80914f7 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -458,7 +458,7 @@ IOR_test_t *ParseCommandLine(int argc, char **argv) * so we'll toss the value in both for now, and sort it out in initialization * after all the arguments are in and we know which it keep. */ - {'G', NULL, "setTimeStampSignature -- set value for time stamp signature/random seed", OPTION_FLAG, 'd', & toggleG}, + {'G', NULL, "setTimeStampSignature -- set value for time stamp signature/random seed", OPTION_OPTIONAL_ARGUMENT, 'd', & toggleG}, {'H', NULL, "showHints -- show hints", OPTION_FLAG, 'd', & initialTestParams.showHints}, {'i', NULL, "repetitions -- number of repetitions of test", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.repetitions}, {'I', NULL, "individualDataSets -- datasets not shared by all procs [not working]", OPTION_FLAG, 'd', & initialTestParams.individualDataSets}, @@ -532,7 +532,7 @@ IOR_test_t *ParseCommandLine(int argc, char **argv) if (memoryPerNode){ initialTestParams.memoryPerNode = NodeMemoryStringToBytes(optarg); } - + const ior_aiori_t * backend = aiori_select(initialTestParams.api); initialTestParams.backend = backend; initialTestParams.apiVersion = backend->get_version(); From c0657866fd183cfce85bf36ae99a2079e6c923fa Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 14 Jul 2018 12:22:36 +0100 Subject: [PATCH 61/80] Minor fixes for stonewalling options. --- src/ior-output.c | 5 +++-- src/ior.c | 11 ++++++----- src/mdtest.c | 14 +++++++++++--- src/utilities.c | 4 +++- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/ior-output.c b/src/ior-output.c index 10a9a86..dcf0c3c 100644 --- a/src/ior-output.c +++ b/src/ior-output.c @@ -392,7 +392,7 @@ void ShowTestStart(IOR_param_t *test) void ShowTestEnd(IOR_test_t *tptr){ if(rank == 0 && tptr->params.stoneWallingWearOut){ - if (tptr->params.stoneWallingStatusFile[0]){ + if (tptr->params.stoneWallingStatusFile){ StoreStoneWallingIterations(tptr->params.stoneWallingStatusFile, tptr->results->pairs_accessed); }else{ fprintf(out_logfile, "Pairs deadlineForStonewallingaccessed: %lld\n", (long long) tptr->results->pairs_accessed); @@ -411,7 +411,8 @@ void ShowSetup(IOR_param_t *params) fprintf(out_logfile, "*** %s ***\n\n", params->debug); } PrintNamedSectionStart("Options"); - PrintKeyVal("api", params->apiVersion); + PrintKeyVal("api", params->api); + PrintKeyVal("apiVersion", params->apiVersion); PrintKeyVal("test filename", params->testFileName); PrintKeyVal("access", params->filePerProc ? "file-per-process" : "single-shared-file"); PrintKeyVal("type", params->collective == FALSE ? "independent" : "collective"); diff --git a/src/ior.c b/src/ior.c index e493d09..69edd43 100755 --- a/src/ior.c +++ b/src/ior.c @@ -1869,10 +1869,11 @@ static IOR_offset_t WriteOrRead(IOR_param_t * test, IOR_results_t * results, voi && ((GetTimeStamp() - startForStonewall) > test->deadlineForStonewalling)); - if(access == READ && test->stoneWallingStatusFile){ + if(test->stoneWallingStatusFile && (access == READ || access == READCHECK)){ test->stoneWallingWearOutIterations = ReadStoneWallingIterations(test->stoneWallingStatusFile); - if(test->stoneWallingWearOutIterations == -1){ - ERR("Could not read back the stonewalling status from the file!"); + if(test->stoneWallingWearOutIterations == -1 && rank == 0){ + fprintf(out_logfile, "WARNING: Could not read back the stonewalling status from the file!"); + test->stoneWallingWearOutIterations = 0; } } @@ -1881,8 +1882,8 @@ static IOR_offset_t WriteOrRead(IOR_param_t * test, IOR_results_t * results, voi dataMoved += WriteOrReadSingle(pairCnt, offsetArray, pretendRank, & transferCount, & errors, test, fd, ioBuffers, access); pairCnt++; - hitStonewall = ((test->deadlineForStonewalling != 0) - && ((GetTimeStamp() - startForStonewall) + hitStonewall = ((test->deadlineForStonewalling != 0 + && (GetTimeStamp() - startForStonewall) > test->deadlineForStonewalling)) || (test->stoneWallingWearOutIterations != 0 && pairCnt == test->stoneWallingWearOutIterations) ; } if (test->stoneWallingWearOut){ diff --git a/src/mdtest.c b/src/mdtest.c index 53250e2..2fe9be4 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -1107,10 +1107,18 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro } }else{ if (stoneWallingStatusFile){ + int64_t expected_items; /* The number of items depends on the stonewalling file */ - items = ReadStoneWallingIterations(stoneWallingStatusFile); - if (verbose >= 1 && rank == 0) { - printf("read stonewall file items: "LLU"\n", items); + expected_items = ReadStoneWallingIterations(stoneWallingStatusFile); + if(expected_items >= 0){ + items = expected_items; + } + if (rank == 0) { + if(expected_items == -1){ + fprintf(out_logfile, "WARNING: could not read stonewall status file\n"); + }else if(verbose >= 1){ + fprintf(out_logfile, "Read stonewall status; items: "LLU"\n", items); + } } } } diff --git a/src/utilities.c b/src/utilities.c index d0357a1..c4a259c 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -581,7 +581,9 @@ int64_t ReadStoneWallingIterations(char * const filename){ }else{ FILE * out = fopen(filename, "r"); if (out == NULL){ - return -1; + data = -1; + MPI_Bcast( & data, 1, MPI_LONG_LONG_INT, 0, mpi_comm_world); + return data; } int ret = fscanf(out, "%lld", & data); if (ret != 1){ From d4ecc76ee89bd552d5e234427fedd77331db4f54 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sat, 14 Jul 2018 16:46:24 +0100 Subject: [PATCH 62/80] Stonewall bugfix for delete/stat. --- src/mdtest.c | 53 ++++++++++++++++------------------------------------ 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/src/mdtest.c b/src/mdtest.c index 2fe9be4..a1d95f7 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -123,6 +123,7 @@ static uint64_t num_dirs_in_tree; * of files in a directory. Make room for that possibility with * a larger variable. */ +static uint64_t items; static uint64_t items_per_dir; static int print_time; static int random_seed; @@ -133,7 +134,6 @@ static int pre_delay; static int unique_dir_per_task; static int time_unique_dir_overhead; static int throttle; -static uint64_t items; static int collective_creates; static size_t write_bytes; static int stone_wall_timer_seconds; @@ -158,8 +158,8 @@ typedef struct{ int stone_wall_timer_seconds; - long long unsigned items_start; - long long unsigned items_done; + uint64_t items_start; + uint64_t items_done; uint64_t items_per_dir; } rank_progress_t; @@ -529,22 +529,14 @@ void mdtest_stat(const int random, const int dirs, const char *path, rank_progre struct stat buf; uint64_t parent_dir, item_num = 0; char item[MAX_PATHLEN], temp[MAX_PATHLEN]; - uint64_t stop; if (( rank == 0 ) && ( verbose >= 1 )) { fprintf( out_logfile, "V-1: Entering mdtest_stat...\n" ); fflush( out_logfile ); } - /* determine the number of items to stat*/ - if (leaf_only) { - stop = items_per_dir * (uint64_t) pow( branch_factor, depth ); - } else { - stop = items; - } - /* iterate over all of the item IDs */ - for (uint64_t i = 0 ; i < stop ; ++i) { + for (uint64_t i = 0 ; i < items ; ++i) { /* * It doesn't make sense to pass the address of the array because that would * be like passing char **. Tested it on a Cray and it seems to work either @@ -565,7 +557,7 @@ void mdtest_stat(const int random, const int dirs, const char *path, rank_progre /* make adjustments if in leaf only mode*/ if (leaf_only) { item_num += items_per_dir * - (num_dirs_in_tree - ( unsigned long long )pow( branch_factor, depth )); + (num_dirs_in_tree - (uint64_t) pow( branch_factor, depth )); } /* create name of file/dir to stat */ @@ -635,7 +627,7 @@ void mdtest_stat(const int random, const int dirs, const char *path, rank_progre /* reads all of the items created as specified by the input parameters */ void mdtest_read(int random, int dirs, char *path) { - uint64_t stop, parent_dir, item_num = 0; + uint64_t parent_dir, item_num = 0; char item[MAX_PATHLEN], temp[MAX_PATHLEN]; void *aiori_fh; @@ -652,15 +644,8 @@ void mdtest_read(int random, int dirs, char *path) { } } - /* determine the number of items to read */ - if (leaf_only) { - stop = items_per_dir * ( unsigned long long )pow( branch_factor, depth ); - } else { - stop = items; - } - /* iterate over all of the item IDs */ - for (uint64_t i = 0 ; i < stop ; ++i) { + for (uint64_t i = 0 ; i < items ; ++i) { /* * It doesn't make sense to pass the address of the array because that would * be like passing char **. Tested it on a Cray and it seems to work either @@ -1025,7 +1010,7 @@ void directory_test(const int iteration, const int ntasks, const char *path, ran int updateStoneWallIterations(int iteration, rank_progress_t * progress, double tstart){ int hit = 0; if (verbose >= 1 ) { - fprintf( out_logfile, "V-1: rank %d stonewall hit with %lld items\n", rank, progress->items_done ); + fprintf( out_logfile, "V-1: rank %d stonewall hit with %lld items\n", rank, (long long) progress->items_done ); fflush( out_logfile ); } progress->items_start = progress->items_done; @@ -1099,10 +1084,9 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro create_remove_items(0, 0, 1, 0, temp_path, 0, progress); // now reset the values progress->stone_wall_timer_seconds = stone_wall_timer_seconds; - items = progress->items_per_dir; } if (stoneWallingStatusFile){ - StoreStoneWallingIterations(stoneWallingStatusFile, progress->items_per_dir); + StoreStoneWallingIterations(stoneWallingStatusFile, progress->items_done); } } }else{ @@ -1112,6 +1096,7 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro expected_items = ReadStoneWallingIterations(stoneWallingStatusFile); if(expected_items >= 0){ items = expected_items; + progress->items_per_dir = items; } if (rank == 0) { if(expected_items == -1){ @@ -2142,7 +2127,7 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * {'n', NULL, "every process will creat/stat/read/remove # directories and files", OPTION_OPTIONAL_ARGUMENT, 'l', & items}, {'N', NULL, "stride # between neighbor tasks for file/dir operation (local=0)", OPTION_OPTIONAL_ARGUMENT, 'd', & nstride}, {'p', NULL, "pre-iteration delay (in seconds)", OPTION_OPTIONAL_ARGUMENT, 'd', & pre_delay}, - {'R', NULL, "randomly stat files", OPTION_FLAG, 'd', & randomize}, + {'R', NULL, "random access to files (only for stat)", OPTION_FLAG, 'd', & randomize}, {0, "random-seed", "random seed for -R", OPTION_OPTIONAL_ARGUMENT, 'd', & random_seed}, {'s', NULL, "stride between the number of tasks for each test", OPTION_OPTIONAL_ARGUMENT, 'd', & stride}, {'S', NULL, "shared file access (file only, no directories)", OPTION_FLAG, 'd', & shared_file}, @@ -2285,8 +2270,8 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * if (branch_factor <= 1) { items_per_dir = items; } else { - items_per_dir = items / pow(branch_factor, depth); - items = items_per_dir * pow(branch_factor, depth); + items_per_dir = (uint64_t) (items / pow(branch_factor, depth)); + items = items_per_dir * (uint64_t) pow(branch_factor, depth); } } else { items_per_dir = items / num_dirs_in_tree; @@ -2298,22 +2283,16 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * if (random_seed > 0) { srand(random_seed); - uint64_t stop = 0; uint64_t s; - if (leaf_only) { - stop = items_per_dir * (uint64_t) pow(branch_factor, depth); - } else { - stop = items; - } - rand_array = (uint64_t *) malloc( stop * sizeof(*rand_array)); + rand_array = (uint64_t *) malloc( items * sizeof(*rand_array)); - for (s=0; s1) { n--; From bb6d4fb2a23eddf266970a7dccc05963a610e442 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sun, 15 Jul 2018 10:02:45 +0100 Subject: [PATCH 63/80] Update create for stonewall. --- src/mdtest.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/mdtest.c b/src/mdtest.c index a1d95f7..08a6f07 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -1013,9 +1013,9 @@ int updateStoneWallIterations(int iteration, rank_progress_t * progress, double fprintf( out_logfile, "V-1: rank %d stonewall hit with %lld items\n", rank, (long long) progress->items_done ); fflush( out_logfile ); } - progress->items_start = progress->items_done; + uint64_t done = progress->items_done; long long unsigned max_iter = 0; - MPI_Allreduce(& progress->items_done, & max_iter, 1, MPI_INT, MPI_MAX, testComm); + MPI_Allreduce(& progress->items_done, & max_iter, 1, MPI_LONG_LONG_INT, MPI_MAX, testComm); summary_table[iteration].stonewall_time[MDTEST_FILE_CREATE_NUM] = MPI_Wtime() - tstart; // continue to the maximum... @@ -1024,13 +1024,16 @@ int updateStoneWallIterations(int iteration, rank_progress_t * progress, double long long sum_accessed = 0; MPI_Reduce(& progress->items_done, & sum_accessed, 1, MPI_LONG_LONG_INT, MPI_SUM, 0, testComm); - if (rank == 0 && items != (sum_accessed / size)) { + if(items != (sum_accessed / size) && rank == 0){ summary_table[iteration].stonewall_item_sum[MDTEST_FILE_CREATE_NUM] = sum_accessed; summary_table[iteration].stonewall_item_min[MDTEST_FILE_CREATE_NUM] = min_accessed * size; - fprintf( out_logfile, "V-1: continue stonewall hit min: %lld max: %lld avg: %.1f \n", min_accessed, max_iter, ((double) sum_accessed) / size); + fprintf( out_logfile, "Continue stonewall hit min: %lld max: %lld avg: %.1f \n", min_accessed, max_iter, ((double) sum_accessed) / size); fflush( out_logfile ); + } + if( done != max_iter ){ hit = 1; } + progress->items_start = done; progress->items_per_dir = max_iter; return hit; @@ -1081,6 +1084,7 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro if (hit){ progress->stone_wall_timer_seconds = 0; + printf("stonewall rank %d: %lld of %lld \n", rank, (long long) progress->items_start, (long long) progress->items_per_dir); create_remove_items(0, 0, 1, 0, temp_path, 0, progress); // now reset the values progress->stone_wall_timer_seconds = stone_wall_timer_seconds; From c7a598a435b35512e9f677ba36eb13f31cc1f214 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sun, 15 Jul 2018 18:37:15 +0100 Subject: [PATCH 64/80] New option parser: Fixed overflow for blocksize. Added warning. --- src/option.c | 7 ++++++- src/parse_options.c | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/option.c b/src/option.c index 831afb2..5d0e661 100644 --- a/src/option.c +++ b/src/option.c @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -324,7 +325,11 @@ int option_parse(int argc, char ** argv, option_help * args, int * printhelp){ break; } case('d'):{ - *(int*) o->variable = string_to_bytes(arg); + int64_t val = string_to_bytes(arg); + if (val > INT_MAX || val < INT_MIN){ + printf("WARNING: parsing the number %s to integer, this produced an overflow!\n", arg); + } + *(int*) o->variable = val; break; } case('H'): diff --git a/src/parse_options.c b/src/parse_options.c index 80914f7..485f4a2 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -440,7 +440,7 @@ IOR_test_t *ParseCommandLine(int argc, char **argv) option_help options [] = { {'a', NULL, "API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI]", OPTION_OPTIONAL_ARGUMENT, 's', & initialTestParams.api}, {'A', NULL, "refNum -- user supplied reference number to include in the summary", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.referenceNumber}, - {'b', NULL, "blockSize -- contiguous bytes to write per task (e.g.: 8, 4k, 2m, 1g)", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.blockSize}, + {'b', NULL, "blockSize -- contiguous bytes to write per task (e.g.: 8, 4k, 2m, 1g)", OPTION_OPTIONAL_ARGUMENT, 'l', & initialTestParams.blockSize}, {'B', NULL, "useO_DIRECT -- uses O_DIRECT for POSIX, bypassing I/O buffers", OPTION_FLAG, 'd', & initialTestParams.useO_DIRECT}, {'c', NULL, "collective -- collective I/O", OPTION_FLAG, 'd', & initialTestParams.collective}, {'C', NULL, "reorderTasks -- changes task ordering to n+1 ordering for readback", OPTION_FLAG, 'd', & initialTestParams.reorderTasks}, From f55761d5d2badd378b76a9e66070f63d2361bcd3 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sun, 15 Jul 2018 19:38:17 +0100 Subject: [PATCH 65/80] Refactored results structure to AoS; allowed to keep results per repeat. --- src/ior-output.c | 52 +++++++++++---------- src/ior.c | 117 ++++++++++++++++++++--------------------------- src/ior.h | 20 ++++---- 3 files changed, 88 insertions(+), 101 deletions(-) diff --git a/src/ior-output.c b/src/ior-output.c index dcf0c3c..2fcf424 100644 --- a/src/ior-output.c +++ b/src/ior-output.c @@ -10,8 +10,8 @@ extern char **environ; -static struct results *bw_values(int reps, IOR_offset_t *agg_file_size, double *vals); -static struct results *ops_values(int reps, IOR_offset_t *agg_file_size, IOR_offset_t transfer_size, double *vals); +static struct results *bw_values(int reps, IOR_results_t * measured, int offset, double *vals); +static struct results *ops_values(int reps, IOR_results_t * measured, int offset, IOR_offset_t transfer_size, double *vals); static double mean_of_array_of_doubles(double *values, int len); static void PPDouble(int leftjustify, double number, char *append); static void PrintNextToken(); @@ -468,21 +468,26 @@ void ShowSetup(IOR_param_t *params) * * operation is typically "write" or "read" */ -void PrintLongSummaryOneOperation(IOR_test_t *test, double *times, char *operation) +static void PrintLongSummaryOneOperation(IOR_test_t *test, int times_offset, char *operation) { IOR_param_t *params = &test->params; IOR_results_t *results = test->results; struct results *bw; struct results *ops; + int reps; if (rank != 0 || verbose < VERBOSE_0) return; reps = params->repetitions; - bw = bw_values(reps, results->aggFileSizeForBW, times); - ops = ops_values(reps, results->aggFileSizeForBW, - params->transferSize, times); + double * times = malloc(sizeof(double)* reps); + for(int i=0; i < reps; i++){ + times[i] = *(double*)((char*) & results[i] + times_offset); + } + + bw = bw_values(reps, results, offsetof(IOR_results_t, aggFileSizeForBW), times); + ops = ops_values(reps, results, offsetof(IOR_results_t, aggFileSizeForBW), params->transferSize, times); if(outputFormat == OUTPUT_DEFAULT){ fprintf(out_resultfile, "%-9s ", operation); @@ -507,7 +512,7 @@ void PrintLongSummaryOneOperation(IOR_test_t *test, double *times, char *operati fprintf(out_resultfile, "%6lld ", params->segmentCount); fprintf(out_resultfile, "%8lld ", params->blockSize); fprintf(out_resultfile, "%8lld ", params->transferSize); - fprintf(out_resultfile, "%9.1f ", (float)results->aggFileSizeForBW[0] / MEBIBYTE); + fprintf(out_resultfile, "%9.1f ", (float)results[0].aggFileSizeForBW / MEBIBYTE); fprintf(out_resultfile, "%3s ", params->api); fprintf(out_resultfile, "%6d", params->referenceNumber); fprintf(out_resultfile, "\n"); @@ -540,7 +545,7 @@ void PrintLongSummaryOneOperation(IOR_test_t *test, double *times, char *operati PrintKeyValDouble("OPsMean", ops->mean); PrintKeyValDouble("OPsSD", ops->sd); PrintKeyValDouble("MeanTime", mean_of_array_of_doubles(times, reps)); - PrintKeyValDouble("xsizeMiB", (double) results->aggFileSizeForBW[0] / MEBIBYTE); + PrintKeyValDouble("xsizeMiB", (double) results[0].aggFileSizeForBW / MEBIBYTE); PrintEndSection(); }else if (outputFormat == OUTPUT_CSV){ @@ -550,17 +555,17 @@ void PrintLongSummaryOneOperation(IOR_test_t *test, double *times, char *operati free(bw); free(ops); + free(times); } void PrintLongSummaryOneTest(IOR_test_t *test) { IOR_param_t *params = &test->params; - IOR_results_t *results = test->results; if (params->writeFile) - PrintLongSummaryOneOperation(test, results->writeTime, "write"); + PrintLongSummaryOneOperation(test, offsetof(IOR_results_t, writeTime), "write"); if (params->readFile) - PrintLongSummaryOneOperation(test, results->readTime, "read"); + PrintLongSummaryOneOperation(test, offsetof(IOR_results_t, readTime), "read"); } void PrintLongSummaryHeader() @@ -625,12 +630,12 @@ void PrintShortSummary(IOR_test_t * test) reps = params->repetitions; - max_write = results->writeTime[0]; - max_read = results->readTime[0]; + max_write = results[0].writeTime; + max_read = results[0].readTime; for (i = 0; i < reps; i++) { - bw = (double)results->aggFileSizeForBW[i]/results->writeTime[i]; + bw = (double)results[i].aggFileSizeForBW / results[i].writeTime; max_write = MAX(bw, max_write); - bw = (double)results->aggFileSizeForBW[i]/results->readTime[i]; + bw = (double)results[i].aggFileSizeForBW / results[i].readTime; max_read = MAX(bw, max_read); } @@ -739,19 +744,19 @@ static void PPDouble(int leftjustify, double number, char *append) -static struct results *bw_values(int reps, IOR_offset_t *agg_file_size, double *vals) +static struct results *bw_values(int reps, IOR_results_t * measured, int offset, double *vals) { struct results *r; int i; - r = (struct results *)malloc(sizeof(struct results) - + (reps * sizeof(double))); + r = (struct results *) malloc(sizeof(struct results) + (reps * sizeof(double))); if (r == NULL) ERR("malloc failed"); r->val = (double *)&r[1]; - for (i = 0; i < reps; i++) { - r->val[i] = (double)agg_file_size[i] / vals[i]; + for (i = 0; i < reps; i++, measured++) { + + r->val[i] = (double) *((IOR_offset_t*) ((char*)measured + offset)) / vals[i]; if (i == 0) { r->min = r->val[i]; r->max = r->val[i]; @@ -772,7 +777,7 @@ static struct results *bw_values(int reps, IOR_offset_t *agg_file_size, double * return r; } -static struct results *ops_values(int reps, IOR_offset_t *agg_file_size, +static struct results *ops_values(int reps, IOR_results_t * measured, int offset, IOR_offset_t transfer_size, double *vals) { @@ -785,8 +790,9 @@ static struct results *ops_values(int reps, IOR_offset_t *agg_file_size, ERR("malloc failed"); r->val = (double *)&r[1]; - for (i = 0; i < reps; i++) { - r->val[i] = (double)agg_file_size[i] / transfer_size / vals[i]; + for (i = 0; i < reps; i++, measured++) { + r->val[i] = (double) *((IOR_offset_t*) ((char*)measured + offset)) + / transfer_size / vals[i]; if (i == 0) { r->min = r->val[i]; r->max = r->val[i]; diff --git a/src/ior.c b/src/ior.c index 69edd43..823afb9 100755 --- a/src/ior.c +++ b/src/ior.c @@ -294,25 +294,25 @@ static void CheckFileSize(IOR_test_t *test, IOR_offset_t dataMoved, int rep) IOR_param_t *params = &test->params; IOR_results_t *results = test->results; - MPI_CHECK(MPI_Allreduce(&dataMoved, &results->aggFileSizeFromXfer[rep], + MPI_CHECK(MPI_Allreduce(&dataMoved, & results[rep].aggFileSizeFromXfer, 1, MPI_LONG_LONG_INT, MPI_SUM, testComm), "cannot total data moved"); if (strcasecmp(params->api, "HDF5") != 0 && strcasecmp(params->api, "NCMPI") != 0) { if (verbose >= VERBOSE_0 && rank == 0) { if ((params->expectedAggFileSize - != results->aggFileSizeFromXfer[rep]) - || (results->aggFileSizeFromStat[rep] - != results->aggFileSizeFromXfer[rep])) { + != results[rep].aggFileSizeFromXfer) + || (results[rep].aggFileSizeFromStat + != results[rep].aggFileSizeFromXfer)) { fprintf(out_logfile, "WARNING: Expected aggregate file size = %lld.\n", (long long) params->expectedAggFileSize); fprintf(out_logfile, "WARNING: Stat() of aggregate file size = %lld.\n", - (long long) results->aggFileSizeFromStat[rep]); + (long long) results[rep].aggFileSizeFromStat); fprintf(out_logfile, "WARNING: Using actual aggregate bytes moved = %lld.\n", - (long long) results->aggFileSizeFromXfer[rep]); + (long long) results[rep].aggFileSizeFromXfer); if(params->deadlineForStonewalling){ fprintf(out_logfile, "WARNING: maybe caused by deadlineForStonewalling\n"); @@ -320,7 +320,7 @@ static void CheckFileSize(IOR_test_t *test, IOR_offset_t dataMoved, int rep) } } } - results->aggFileSizeForBW[rep] = results->aggFileSizeFromXfer[rep]; + results[rep].aggFileSizeForBW = results[rep].aggFileSizeFromXfer; } /* @@ -491,54 +491,30 @@ static void aligned_buffer_free(void *buf) free(*(void **)((char *)buf - sizeof(char *))); } +static void* safeMalloc(uint64_t size){ + void * d = malloc(size); + if (d == NULL){ + ERR("Could not malloc an array"); + } + memset(d, 0, size); + return d; +} + static void AllocResults(IOR_test_t *test) { - int reps; - if (test->results != NULL) - return; - - reps = test->params.repetitions; - test->results = (IOR_results_t *)malloc(sizeof(IOR_results_t)); - if (test->results == NULL) - ERR("malloc of IOR_results_t failed"); - - test->results->writeTime = (double *)malloc(reps * sizeof(double)); - if (test->results->writeTime == NULL) - ERR("malloc of writeTime array failed"); - memset(test->results->writeTime, 0, reps * sizeof(double)); - - test->results->readTime = (double *)malloc(reps * sizeof(double)); - if (test->results->readTime == NULL) - ERR("malloc of readTime array failed"); - memset(test->results->readTime, 0, reps * sizeof(double)); - - test->results->aggFileSizeFromStat = - (IOR_offset_t *)malloc(reps * sizeof(IOR_offset_t)); - if (test->results->aggFileSizeFromStat == NULL) - ERR("malloc of aggFileSizeFromStat failed"); - - test->results->aggFileSizeFromXfer = - (IOR_offset_t *)malloc(reps * sizeof(IOR_offset_t)); - if (test->results->aggFileSizeFromXfer == NULL) - ERR("malloc of aggFileSizeFromXfer failed"); - - test->results->aggFileSizeForBW = - (IOR_offset_t *)malloc(reps * sizeof(IOR_offset_t)); - if (test->results->aggFileSizeForBW == NULL) - ERR("malloc of aggFileSizeForBW failed"); + int reps; + if (test->results != NULL) + return; + reps = test->params.repetitions; + test->results = (IOR_results_t *) safeMalloc(sizeof(IOR_results_t) * reps); } void FreeResults(IOR_test_t *test) { - if (test->results != NULL) { - free(test->results->aggFileSizeFromStat); - free(test->results->aggFileSizeFromXfer); - free(test->results->aggFileSizeForBW); - free(test->results->readTime); - free(test->results->writeTime); - free(test->results); - } + if (test->results != NULL) { + free(test->results); + } } @@ -908,11 +884,11 @@ static void ReduceIterResults(IOR_test_t *test, double **timer, int rep, } if (access == WRITE) { totalTime = reduced[5] - reduced[0]; - test->results->writeTime[rep] = totalTime; + test->results[rep].writeTime = totalTime; diff_subset = &diff[0]; } else { /* READ */ totalTime = reduced[11] - reduced[6]; - test->results->readTime[rep] = totalTime; + test->results[rep].readTime = totalTime; diff_subset = &diff[3]; } @@ -920,7 +896,7 @@ static void ReduceIterResults(IOR_test_t *test, double **timer, int rep, return; } - bw = (double)test->results->aggFileSizeForBW[rep] / totalTime; + bw = (double)test->results[rep].aggFileSizeForBW / totalTime; PrintReducedResult(test, access, bw, diff_subset, totalTime, rep); } @@ -1233,6 +1209,7 @@ static void TestIoSys(IOR_test_t *test) startTime = GetTimeStamp(); /* loop over test iterations */ + uint64_t params_saved_wearout = params->stoneWallingWearOutIterations; for (rep = 0; rep < params->repetitions; rep++) { PrintRepeatStart(); /* Get iteration start time in seconds in task 0 and broadcast to @@ -1280,6 +1257,8 @@ static void TestIoSys(IOR_test_t *test) RemoveFile(testFileName, params->filePerProc, params); } + + params->stoneWallingWearOutIterations = params_saved_wearout; MPI_CHECK(MPI_Barrier(testComm), "barrier error"); params->open = WRITE; timer[0][rep] = GetTimeStamp(); @@ -1294,7 +1273,7 @@ static void TestIoSys(IOR_test_t *test) CurrentTimeString()); } timer[2][rep] = GetTimeStamp(); - dataMoved = WriteOrRead(params, results, fd, WRITE, &ioBuffers); + dataMoved = WriteOrRead(params, & results[rep], fd, WRITE, &ioBuffers); if (params->verbose >= VERBOSE_4) { fprintf(out_logfile, "* data moved = %llu\n", dataMoved); fflush(out_logfile); @@ -1310,7 +1289,7 @@ static void TestIoSys(IOR_test_t *test) MPI_CHECK(MPI_Barrier(testComm), "barrier error"); /* get the size of the file just written */ - results->aggFileSizeFromStat[rep] = + results[rep].aggFileSizeFromStat = backend->get_file_size(params, testComm, testFileName); /* check if stat() of file doesn't equal expected file size, @@ -1323,6 +1302,11 @@ static void TestIoSys(IOR_test_t *test) if (params->outlierThreshold) { CheckForOutliers(params, timer, rep, WRITE); } + + /* check if in this round we run write with stonewalling */ + if(params->deadlineForStonewalling > 0){ + params->stoneWallingWearOutIterations = results[rep].pairs_accessed; + } } /* @@ -1349,7 +1333,7 @@ static void TestIoSys(IOR_test_t *test) GetTestFileName(testFileName, params); params->open = WRITECHECK; fd = backend->open(testFileName, params); - dataMoved = WriteOrRead(params, results, fd, WRITECHECK, &ioBuffers); + dataMoved = WriteOrRead(params, & results[rep], fd, WRITECHECK, &ioBuffers); backend->close(fd, params); rankOffset = 0; } @@ -1357,6 +1341,14 @@ static void TestIoSys(IOR_test_t *test) * read the file(s), getting timing between I/O calls */ if ((params->readFile || params->checkRead ) && !test_time_elapsed(params, startTime)) { + /* check for stonewall */ + if(params->stoneWallingStatusFile){ + params->stoneWallingWearOutIterations = ReadStoneWallingIterations(params->stoneWallingStatusFile); + if(params->stoneWallingWearOutIterations == -1 && rank == 0){ + fprintf(out_logfile, "WARNING: Could not read back the stonewalling status from the file!"); + params->stoneWallingWearOutIterations = 0; + } + } int operation_flag = READ; if ( params->checkRead ){ // actually read and then compare the buffer @@ -1420,7 +1412,7 @@ static void TestIoSys(IOR_test_t *test) CurrentTimeString()); } timer[8][rep] = GetTimeStamp(); - dataMoved = WriteOrRead(params, results, fd, operation_flag, &ioBuffers); + dataMoved = WriteOrRead(params, & results[rep], fd, operation_flag, &ioBuffers); timer[9][rep] = GetTimeStamp(); if (params->intraTestBarriers) MPI_CHECK(MPI_Barrier(testComm), @@ -1430,7 +1422,7 @@ static void TestIoSys(IOR_test_t *test) timer[11][rep] = GetTimeStamp(); /* get the size of the file just read */ - results->aggFileSizeFromStat[rep] = + results[rep].aggFileSizeFromStat = backend->get_file_size(params, testComm, testFileName); @@ -1863,19 +1855,8 @@ static IOR_offset_t WriteOrRead(IOR_param_t * test, IOR_results_t * results, voi offsetArray = GetOffsetArraySequential(test, pretendRank); } - /* check for stonewall */ startForStonewall = GetTimeStamp(); - hitStonewall = ((test->deadlineForStonewalling != 0) - && ((GetTimeStamp() - startForStonewall) - > test->deadlineForStonewalling)); - - if(test->stoneWallingStatusFile && (access == READ || access == READCHECK)){ - test->stoneWallingWearOutIterations = ReadStoneWallingIterations(test->stoneWallingStatusFile); - if(test->stoneWallingWearOutIterations == -1 && rank == 0){ - fprintf(out_logfile, "WARNING: Could not read back the stonewalling status from the file!"); - test->stoneWallingWearOutIterations = 0; - } - } + hitStonewall = 0; /* loop over offsets to access */ while ((offsetArray[pairCnt] != -1) && !hitStonewall ) { diff --git a/src/ior.h b/src/ior.h index 24a6d53..2ef7a32 100755 --- a/src/ior.h +++ b/src/ior.h @@ -205,24 +205,24 @@ typedef struct /* each pointer is to an array, each of length equal to the number of repetitions in the test */ typedef struct { - double *writeTime; - double *readTime; - int errors; + double writeTime; + double readTime; + int errors; size_t pairs_accessed; // number of I/Os done, useful for deadlineForStonewalling - double stonewall_time; - long long stonewall_min_data_accessed; - long long stonewall_avg_data_accessed; + double stonewall_time; + long long stonewall_min_data_accessed; + long long stonewall_avg_data_accessed; - IOR_offset_t *aggFileSizeFromStat; - IOR_offset_t *aggFileSizeFromXfer; - IOR_offset_t *aggFileSizeForBW; + IOR_offset_t aggFileSizeFromStat; + IOR_offset_t aggFileSizeFromXfer; + IOR_offset_t aggFileSizeForBW; } IOR_results_t; /* define the queuing structure for the test parameters */ typedef struct IOR_test_t { IOR_param_t params; - IOR_results_t *results; + IOR_results_t *results; /* This is an array of reps times IOR_results_t */ struct IOR_test_t *next; } IOR_test_t; From 580fb8cdf7fe58555d8057fa755331dc88ae930c Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Sun, 15 Jul 2018 20:23:10 +0100 Subject: [PATCH 66/80] Bugfix offsetof; thanks George for reporting. --- src/ior-output.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ior-output.c b/src/ior-output.c index 2fcf424..27810d7 100644 --- a/src/ior-output.c +++ b/src/ior-output.c @@ -3,6 +3,7 @@ #endif #include +#include /* needed for offsetof on some compilers */ #include "ior.h" #include "ior-internal.h" From 0870ad78b3b022bcd30392a8990ba4e5090d6e34 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Thu, 26 Jul 2018 10:37:01 +0100 Subject: [PATCH 67/80] Support shared large runs with limited number of files per directory by creating one directory for a bunch of files. Therefore, it uses the -I argument (number of files per directory) and divides it by -n (items). This will be a number of sub directories that are created on the top-level. --- src/mdtest.c | 399 +++++++++++++++++++++++++++++---------------------- 1 file changed, 230 insertions(+), 169 deletions(-) diff --git a/src/mdtest.c b/src/mdtest.c index 08a6f07..a9cc1ed 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -83,7 +83,6 @@ static int size; static uint64_t *rand_array; static char testdir[MAX_PATHLEN]; static char testdirpath[MAX_PATHLEN]; -static char top_dir[MAX_PATHLEN]; static char base_tree_name[MAX_PATHLEN]; static char **filenames; static char hostname[MAX_PATHLEN]; @@ -125,6 +124,7 @@ static uint64_t num_dirs_in_tree; */ static uint64_t items; static uint64_t items_per_dir; +static int directory_loops; static int print_time; static int random_seed; static int shared_file; @@ -219,6 +219,15 @@ void parse_dirpath(char *dirpath_arg) { } } +static void prep_testdir(int j, int dir_iter){ + int pos = sprintf(testdir, "%s", testdirpath); + if ( testdir[strlen( testdir ) - 1] != '/' ) { + pos += sprintf(& testdir[pos], "/"); + } + pos += sprintf(& testdir[pos], "%s", TEST_DIR); + pos += sprintf(& testdir[pos], ".%d-%d", j, dir_iter); +} + /* * This function copies the unique directory name for a given option to * the "to" parameter. Some memory must be allocated to the "to" parameter. @@ -232,15 +241,15 @@ void unique_dir_access(int opt, char *to) { if (opt == MK_UNI_DIR) { MPI_Barrier(testComm); - strcpy( to, unique_chdir_dir ); + sprintf( to, "%s/%s", testdir, unique_chdir_dir ); } else if (opt == STAT_SUB_DIR) { - strcpy( to, unique_stat_dir ); + sprintf( to, "%s/%s", testdir, unique_stat_dir ); } else if (opt == READ_SUB_DIR) { - strcpy( to, unique_read_dir ); + sprintf( to, "%s/%s", testdir, unique_read_dir ); } else if (opt == RM_SUB_DIR) { - strcpy( to, unique_rm_dir ); + sprintf( to, "%s/%s", testdir, unique_rm_dir ); } else if (opt == RM_UNI_DIR) { - strcpy( to, unique_rm_uni_dir ); + sprintf( to, "%s/%s", testdir, unique_rm_uni_dir ); } } @@ -525,7 +534,7 @@ void create_remove_items(int currDepth, const int dirs, const int create, const } /* stats all of the items created as specified by the input parameters */ -void mdtest_stat(const int random, const int dirs, const char *path, rank_progress_t * progress) { +void mdtest_stat(const int random, const int dirs, const long dir_iter, const char *path, rank_progress_t * progress) { struct stat buf; uint64_t parent_dir, item_num = 0; char item[MAX_PATHLEN], temp[MAX_PATHLEN]; @@ -535,8 +544,14 @@ void mdtest_stat(const int random, const int dirs, const char *path, rank_progre fflush( out_logfile ); } + uint64_t stop_items = items; + + if( directory_loops != 1 ){ + stop_items = items_per_dir; + } + /* iterate over all of the item IDs */ - for (uint64_t i = 0 ; i < items ; ++i) { + for (uint64_t i = 0 ; i < stop_items ; ++i) { /* * It doesn't make sense to pass the address of the array because that would * be like passing char **. Tested it on a Cray and it seems to work either @@ -547,6 +562,7 @@ void mdtest_stat(const int random, const int dirs, const char *path, rank_progre memset(item, 0, MAX_PATHLEN); memset(temp, 0, MAX_PATHLEN); + /* determine the item number to stat */ if (random) { item_num = rand_array[i]; @@ -626,7 +642,7 @@ void mdtest_stat(const int random, const int dirs, const char *path, rank_progre /* reads all of the items created as specified by the input parameters */ -void mdtest_read(int random, int dirs, char *path) { +void mdtest_read(int random, int dirs, const long dir_iter, char *path) { uint64_t parent_dir, item_num = 0; char item[MAX_PATHLEN], temp[MAX_PATHLEN]; void *aiori_fh; @@ -644,8 +660,14 @@ void mdtest_read(int random, int dirs, char *path) { } } + uint64_t stop_items = items; + + if( directory_loops != 1 ){ + stop_items = items_per_dir; + } + /* iterate over all of the item IDs */ - for (uint64_t i = 0 ; i < items ; ++i) { + for (uint64_t i = 0 ; i < stop_items ; ++i) { /* * It doesn't make sense to pass the address of the array because that would * be like passing char **. Tested it on a Cray and it seems to work either @@ -830,13 +852,15 @@ void directory_test(const int iteration, const int ntasks, const char *path, ran /* create phase */ if(create_only) { + for (int dir_iter = 0; dir_iter < directory_loops; dir_iter ++){ + prep_testdir(iteration, dir_iter); if (unique_dir_per_task) { unique_dir_access(MK_UNI_DIR, temp_path); if (!time_unique_dir_overhead) { offset_timers(t, 0); } } else { - strcpy( temp_path, path ); + sprintf( temp_path, "%s/%s", testdir, path ); } if (verbose >= 3 && rank == 0) { @@ -853,6 +877,7 @@ void directory_test(const int iteration, const int ntasks, const char *path, ran /* create directories */ create_remove_items(0, 1, 1, 0, temp_path, 0, progress); } + } } if (barriers) { @@ -862,13 +887,15 @@ void directory_test(const int iteration, const int ntasks, const char *path, ran /* stat phase */ if (stat_only) { + for (int dir_iter = 0; dir_iter < directory_loops; dir_iter ++){ + prep_testdir(iteration, dir_iter); if (unique_dir_per_task) { unique_dir_access(STAT_SUB_DIR, temp_path); if (!time_unique_dir_overhead) { offset_timers(t, 1); } } else { - strcpy( temp_path, path ); + sprintf( temp_path, "%s/%s", testdir, path ); } if (verbose >= 3 && rank == 0) { @@ -878,10 +905,11 @@ void directory_test(const int iteration, const int ntasks, const char *path, ran /* stat directories */ if (random_seed > 0) { - mdtest_stat(1, 1, temp_path, progress); + mdtest_stat(1, 1, dir_iter, temp_path, progress); } else { - mdtest_stat(0, 1, temp_path, progress); + mdtest_stat(0, 1, dir_iter, temp_path, progress); } + } } if (barriers) { @@ -891,13 +919,15 @@ void directory_test(const int iteration, const int ntasks, const char *path, ran /* read phase */ if (read_only) { + for (int dir_iter = 0; dir_iter < directory_loops; dir_iter ++){ + prep_testdir(iteration, dir_iter); if (unique_dir_per_task) { unique_dir_access(READ_SUB_DIR, temp_path); if (!time_unique_dir_overhead) { offset_timers(t, 2); } } else { - strcpy( temp_path, path ); + sprintf( temp_path, "%s/%s", testdir, path ); } if (verbose >= 3 && rank == 0) { @@ -911,6 +941,7 @@ void directory_test(const int iteration, const int ntasks, const char *path, ran } else { ; /* N/A */ } + } } if (barriers) { @@ -919,13 +950,15 @@ void directory_test(const int iteration, const int ntasks, const char *path, ran t[3] = MPI_Wtime(); if (remove_only) { + for (int dir_iter = 0; dir_iter < directory_loops; dir_iter ++){ + prep_testdir(iteration, dir_iter); if (unique_dir_per_task) { unique_dir_access(RM_SUB_DIR, temp_path); if (!time_unique_dir_overhead) { offset_timers(t, 3); } } else { - strcpy( temp_path, path ); + sprintf( temp_path, "%s/%s", testdir, path ); } if (verbose >= 3 && rank == 0) { @@ -941,6 +974,7 @@ void directory_test(const int iteration, const int ntasks, const char *path, ran } else { create_remove_items(0, 1, 0, 0, temp_path, 0, progress); } + } } if (barriers) { @@ -952,7 +986,7 @@ void directory_test(const int iteration, const int ntasks, const char *path, ran if (unique_dir_per_task) { unique_dir_access(RM_UNI_DIR, temp_path); } else { - strcpy( temp_path, path ); + sprintf( temp_path, "%s/%s", testdir, path ); } if (verbose >= 3 && rank == 0) { @@ -1055,13 +1089,16 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro /* create phase */ if (create_only ) { + for (int dir_iter = 0; dir_iter < directory_loops; dir_iter ++){ + prep_testdir(iteration, dir_iter); + if (unique_dir_per_task) { unique_dir_access(MK_UNI_DIR, temp_path); if (!time_unique_dir_overhead) { offset_timers(t, 0); } } else { - strcpy( temp_path, path ); + sprintf( temp_path, "%s/%s", testdir, path ); } if (verbose >= 3 && rank == 0) { @@ -1093,6 +1130,7 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro StoreStoneWallingIterations(stoneWallingStatusFile, progress->items_done); } } + } }else{ if (stoneWallingStatusFile){ int64_t expected_items; @@ -1119,13 +1157,15 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro /* stat phase */ if (stat_only ) { + for (int dir_iter = 0; dir_iter < directory_loops; dir_iter ++){ + prep_testdir(iteration, dir_iter); if (unique_dir_per_task) { unique_dir_access(STAT_SUB_DIR, temp_path); if (!time_unique_dir_overhead) { offset_timers(t, 1); } } else { - strcpy( temp_path, path ); + sprintf( temp_path, "%s/%s", testdir, path ); } if (verbose >= 3 && rank == 0) { @@ -1134,7 +1174,8 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro } /* stat files */ - mdtest_stat((random_seed > 0 ? 1 : 0), 0, temp_path, progress); + mdtest_stat((random_seed > 0 ? 1 : 0), 0, dir_iter, temp_path, progress); + } } if (barriers) { @@ -1144,13 +1185,15 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro /* read phase */ if (read_only ) { + for (int dir_iter = 0; dir_iter < directory_loops; dir_iter ++){ + prep_testdir(iteration, dir_iter); if (unique_dir_per_task) { unique_dir_access(READ_SUB_DIR, temp_path); if (!time_unique_dir_overhead) { offset_timers(t, 2); } } else { - strcpy( temp_path, path ); + sprintf( temp_path, "%s/%s", testdir, path ); } if (verbose >= 3 && rank == 0) { @@ -1160,10 +1203,11 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro /* read files */ if (random_seed > 0) { - mdtest_read(1,0,temp_path); + mdtest_read(1,0, dir_iter, temp_path); } else { - mdtest_read(0,0,temp_path); + mdtest_read(0,0, dir_iter, temp_path); } + } } if (barriers) { @@ -1172,13 +1216,15 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro t[3] = MPI_Wtime(); if (remove_only) { + for (int dir_iter = 0; dir_iter < directory_loops; dir_iter ++){ + prep_testdir(iteration, dir_iter); if (unique_dir_per_task) { unique_dir_access(RM_SUB_DIR, temp_path); if (!time_unique_dir_overhead) { offset_timers(t, 3); } } else { - strcpy( temp_path, path ); + sprintf( temp_path, "%s/%s", testdir, path ); } if (verbose >= 3 && rank == 0) { @@ -1193,6 +1239,7 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro } else { create_remove_items(0, 0, 0, 0, temp_path, 0, progress); } + } } if (barriers) { @@ -1599,7 +1646,13 @@ void valid_tests() { } /* check for valid number of items */ if ((items > 0) && (items_per_dir > 0)) { - FAIL("only specify the number of items or the number of items per directory"); + if(unique_dir_per_task){ + FAIL("only specify the number of items or the number of items per directory"); + }else if( items % items_per_dir != 0){ + FAIL("items must be a multiple of items per directory"); + }else if( stone_wall_timer_seconds != 0){ + FAIL("items + items_per_dir can only be set without stonewalling"); + } } } @@ -1738,7 +1791,7 @@ void create_remove_directory_tree(int create, } if (-1 == backend->mkdir (dir, DIRMODE, ¶m)) { - //FAIL("Unable to create directory"); + fprintf(out_logfile, "error could not create directory \"%s\"\n", dir); } } @@ -1813,82 +1866,84 @@ static void mdtest_iteration(int i, int j, MPI_Group testgroup, mdtest_results_t fflush(out_logfile); } - int pos = sprintf(testdir, "%s", testdirpath); - if ( testdir[strlen( testdir ) - 1] != '/' ) { - pos += sprintf(& testdir[pos], "/"); - } - pos += sprintf(& testdir[pos], "%s", TEST_DIR); - pos += sprintf(& testdir[pos], ".%d", j); + for (int dir_iter = 0; dir_iter < directory_loops; dir_iter ++){ + prep_testdir(j, dir_iter); - if (verbose >= 2 && rank == 0) { - fprintf(out_logfile, "V-2: main (for j loop): making testdir, \"%s\"\n", testdir ); - fflush( out_logfile ); - } - if ((rank < path_count) && backend->access(testdir, F_OK, ¶m) != 0) { - if (backend->mkdir(testdir, DIRMODE, ¶m) != 0) { - FAIL("Unable to create test directory"); - } + if (verbose >= 2 && rank == 0) { + fprintf(out_logfile, "V-2: main (for j loop): making testdir, \"%s\"\n", testdir ); + fflush( out_logfile ); + } + if ((rank < path_count) && backend->access(testdir, F_OK, ¶m) != 0) { + if (backend->mkdir(testdir, DIRMODE, ¶m) != 0) { + FAIL("Unable to create test directory"); + } + } } - /* create hierarchical directory structure */ - MPI_Barrier(testComm); if (create_only) { + /* create hierarchical directory structure */ + MPI_Barrier(testComm); + startCreate = MPI_Wtime(); - if (unique_dir_per_task) { - if (collective_creates && (rank == 0)) { - /* - * This is inside two loops, one of which already uses "i" and the other uses "j". - * I don't know how this ever worked. I'm changing this loop to use "k". - */ - for (k=0; k= 3 && rank == 0) { - fprintf(out_logfile, - "V-3: main (create hierarchical directory loop-collective): Calling create_remove_directory_tree with \"%s\"\n", - testdir ); - fflush( out_logfile ); - } + if (unique_dir_per_task) { + if (collective_creates && (rank == 0)) { + /* + * This is inside two loops, one of which already uses "i" and the other uses "j". + * I don't know how this ever worked. I'm changing this loop to use "k". + */ + for (k=0; k= 3 && rank == 0) { - fprintf(out_logfile, - "V-3: main (create hierarchical directory loop-!collective_creates): Calling create_remove_directory_tree with \"%s\"\n", - testdir ); - fflush( out_logfile ); - } + if (verbose >= 3 && rank == 0) { + fprintf(out_logfile, + "V-3: main (create hierarchical directory loop-collective): Calling create_remove_directory_tree with \"%s\"\n", + testdir ); + fflush( out_logfile ); + } - /* - * Let's pass in the path to the directory we most recently made so that we can use - * full paths in the other calls. - */ - create_remove_directory_tree(1, 0, testdir, 0, progress); - } - } else { - if (rank == 0) { - if (verbose >= 3 && rank == 0) { - fprintf(out_logfile, - "V-3: main (create hierarchical directory loop-!unque_dir_per_task): Calling create_remove_directory_tree with \"%s\"\n", - testdir ); - fflush( out_logfile ); - } + /* + * Let's pass in the path to the directory we most recently made so that we can use + * full paths in the other calls. + */ + create_remove_directory_tree(1, 0, testdir, 0, progress); + if(CHECK_STONE_WALL(progress)){ + size = k; + break; + } + } + } else if (!collective_creates) { + if (verbose >= 3 && rank == 0) { + fprintf(out_logfile, + "V-3: main (create hierarchical directory loop-!collective_creates): Calling create_remove_directory_tree with \"%s\"\n", + testdir ); + fflush( out_logfile ); + } - /* - * Let's pass in the path to the directory we most recently made so that we can use - * full paths in the other calls. - */ - create_remove_directory_tree(1, 0 , testdir, 0, progress); - } + /* + * Let's pass in the path to the directory we most recently made so that we can use + * full paths in the other calls. + */ + create_remove_directory_tree(1, 0, testdir, 0, progress); + } + } else { + if (rank == 0) { + if (verbose >= 3 && rank == 0) { + fprintf(out_logfile, + "V-3: main (create hierarchical directory loop-!unque_dir_per_task): Calling create_remove_directory_tree with \"%s\"\n", + testdir ); + fflush( out_logfile ); + } + + /* + * Let's pass in the path to the directory we most recently made so that we can use + * full paths in the other calls. + */ + create_remove_directory_tree(1, 0 , testdir, 0, progress); + } + } } MPI_Barrier(testComm); endCreate = MPI_Wtime(); @@ -1903,12 +1958,12 @@ static void mdtest_iteration(int i, int j, MPI_Group testgroup, mdtest_results_t fflush(out_logfile); } } - sprintf(unique_mk_dir, "%s/%s.0", testdir, base_tree_name); - sprintf(unique_chdir_dir, "%s/%s.0", testdir, base_tree_name); - sprintf(unique_stat_dir, "%s/%s.0", testdir, base_tree_name); - sprintf(unique_read_dir, "%s/%s.0", testdir, base_tree_name); - sprintf(unique_rm_dir, "%s/%s.0", testdir, base_tree_name); - sprintf(unique_rm_uni_dir, "%s", testdir); + sprintf(unique_mk_dir, "%s.0", base_tree_name); + sprintf(unique_chdir_dir, "%s.0", base_tree_name); + sprintf(unique_stat_dir, "%s.0", base_tree_name); + sprintf(unique_read_dir, "%s.0", base_tree_name); + sprintf(unique_rm_dir, "%s.0", base_tree_name); + unique_rm_uni_dir[0] = 0; if (!unique_dir_per_task) { if (verbose >= 3 && rank == 0) { @@ -1925,19 +1980,13 @@ static void mdtest_iteration(int i, int j, MPI_Group testgroup, mdtest_results_t sprintf(rm_name, "mdtest.%d.", (rank+(3*nstride))%i); } if (unique_dir_per_task) { - sprintf(unique_mk_dir, "%s/mdtest_tree.%d.0", testdir, - (rank+(0*nstride))%i); - sprintf(unique_chdir_dir, "%s/mdtest_tree.%d.0", testdir, - (rank+(1*nstride))%i); - sprintf(unique_stat_dir, "%s/mdtest_tree.%d.0", testdir, - (rank+(2*nstride))%i); - sprintf(unique_read_dir, "%s/mdtest_tree.%d.0", testdir, - (rank+(3*nstride))%i); - sprintf(unique_rm_dir, "%s/mdtest_tree.%d.0", testdir, - (rank+(4*nstride))%i); - sprintf(unique_rm_uni_dir, "%s", testdir); + sprintf(unique_mk_dir, "mdtest_tree.%d.0", (rank+(0*nstride))%i); + sprintf(unique_chdir_dir, "mdtest_tree.%d.0", (rank+(1*nstride))%i); + sprintf(unique_stat_dir, "mdtest_tree.%d.0", (rank+(2*nstride))%i); + sprintf(unique_read_dir, "mdtest_tree.%d.0", (rank+(3*nstride))%i); + sprintf(unique_rm_dir, "mdtest_tree.%d.0", (rank+(4*nstride))%i); + unique_rm_uni_dir[0] = 0; } - strcpy(top_dir, unique_mk_dir); if (verbose >= 3 && rank == 0) { fprintf(out_logfile, "V-3: main: Copied unique_mk_dir, \"%s\", to topdir\n", unique_mk_dir ); @@ -1969,61 +2018,64 @@ static void mdtest_iteration(int i, int j, MPI_Group testgroup, mdtest_results_t MPI_Barrier(testComm); if (remove_only) { startCreate = MPI_Wtime(); - if (unique_dir_per_task) { - if (collective_creates && (rank == 0)) { - /* - * This is inside two loops, one of which already uses "i" and the other uses "j". - * I don't know how this ever worked. I'm changing this loop to use "k". - */ - for (k=0; k= 3 && rank == 0) { - fprintf(out_logfile, - "V-3: main (remove hierarchical directory loop-collective): Calling create_remove_directory_tree with \"%s\"\n", - testdir ); - fflush( out_logfile ); - } + if (verbose >= 3 && rank == 0) { + fprintf(out_logfile, + "V-3: main (remove hierarchical directory loop-collective): Calling create_remove_directory_tree with \"%s\"\n", + testdir ); + fflush( out_logfile ); + } - /* - * Let's pass in the path to the directory we most recently made so that we can use - * full paths in the other calls. - */ - create_remove_directory_tree(0, 0, testdir, 0, progress); - if(CHECK_STONE_WALL(progress)){ - size = k; - break; - } - } - } else if (!collective_creates) { - if (verbose >= 3 && rank == 0) { - fprintf(out_logfile, - "V-3: main (remove hierarchical directory loop-!collective): Calling create_remove_directory_tree with \"%s\"\n", - testdir ); - fflush( out_logfile ); - } + /* + * Let's pass in the path to the directory we most recently made so that we can use + * full paths in the other calls. + */ + create_remove_directory_tree(0, 0, testdir, 0, progress); + if(CHECK_STONE_WALL(progress)){ + size = k; + break; + } + } + } else if (!collective_creates) { + if (verbose >= 3 && rank == 0) { + fprintf(out_logfile, + "V-3: main (remove hierarchical directory loop-!collective): Calling create_remove_directory_tree with \"%s\"\n", + testdir ); + fflush( out_logfile ); + } - /* - * Let's pass in the path to the directory we most recently made so that we can use - * full paths in the other calls. - */ - create_remove_directory_tree(0, 0, testdir, 0, progress); - } - } else { - if (rank == 0) { - if (verbose >= 3 && rank == 0) { - fprintf(out_logfile, - "V-3: main (remove hierarchical directory loop-!unique_dir_per_task): Calling create_remove_directory_tree with \"%s\"\n", - testdir ); - fflush( out_logfile ); - } + /* + * Let's pass in the path to the directory we most recently made so that we can use + * full paths in the other calls. + */ + create_remove_directory_tree(0, 0, testdir, 0, progress); + } + } else { + if (rank == 0) { + if (verbose >= 3 && rank == 0) { + fprintf(out_logfile, + "V-3: main (remove hierarchical directory loop-!unique_dir_per_task): Calling create_remove_directory_tree with \"%s\"\n", + testdir ); + fflush( out_logfile ); + } - /* - * Let's pass in the path to the directory we most recently made so that we can use - * full paths in the other calls. - */ - create_remove_directory_tree(0, 0 , testdir, 0, progress); - } + /* + * Let's pass in the path to the directory we most recently made so that we can use + * full paths in the other calls. + */ + create_remove_directory_tree(0, 0 , testdir, 0, progress); + } + } } MPI_Barrier(testComm); @@ -2043,11 +2095,14 @@ static void mdtest_iteration(int i, int j, MPI_Group testgroup, mdtest_results_t fflush( out_logfile ); } - if ((rank < path_count) && backend->access(testdir, F_OK, ¶m) == 0) { - //if (( rank == 0 ) && access(testdir, F_OK) == 0) { - if (backend->rmdir(testdir, ¶m) == -1) { - FAIL("unable to remove directory"); - } + for (int dir_iter = 0; dir_iter < directory_loops; dir_iter ++){ + prep_testdir(j, dir_iter); + if ((rank < path_count) && backend->access(testdir, F_OK, ¶m) == 0) { + //if (( rank == 0 ) && access(testdir, F_OK) == 0) { + if (backend->rmdir(testdir, ¶m) == -1) { + FAIL("unable to remove directory"); + } + } } } else { summary_table->rate[9] = 0; @@ -2215,7 +2270,11 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * } random_seed += rank; } - + if ((items > 0) && (items_per_dir > 0) && (! unique_dir_per_task)) { + directory_loops = items / items_per_dir; + }else{ + directory_loops = 1; + } valid_tests(); if (( rank == 0 ) && ( verbose >= 1 )) { @@ -2268,7 +2327,9 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * } } if (items_per_dir > 0) { - items = items_per_dir * num_dirs_in_tree; + if(unique_dir_per_task){ + items = items_per_dir * num_dirs_in_tree; + } } else { if (leaf_only) { if (branch_factor <= 1) { From e708681f19caa5556e7d70ec6ca1ac972fc14178 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Mon, 6 Aug 2018 18:38:34 +0100 Subject: [PATCH 68/80] Option: check for missing arg. --- src/option.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/option.c b/src/option.c index 5d0e661..1e646af 100644 --- a/src/option.c +++ b/src/option.c @@ -309,6 +309,12 @@ int option_parse(int argc, char ** argv, option_help * args, int * printhelp){ } } + if(arg == NULL){ + const char str[] = {o->shortVar, 0}; + printf("Error, argument missing for option %s\n", (o->longVar != NULL) ? o->longVar : str); + exit(1); + } + switch(o->type){ case('p'):{ // call the function in the variable From 44ce8e5aaf8e60c4d655b49d968f8d77bd393064 Mon Sep 17 00:00:00 2001 From: Jean-Yves VET Date: Mon, 6 Aug 2018 12:04:26 +0200 Subject: [PATCH 69/80] Add backend for DDN's Infinite Memory Engine (IME) This patch adds the support of IME Native interface as a new AIORI backend. --- configure.ac | 11 ++ doc/USER_GUIDE | 8 +- src/Makefile.am | 7 ++ src/aiori-IME.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++++ src/aiori.c | 3 + src/aiori.h | 1 + 6 files changed, 299 insertions(+), 4 deletions(-) create mode 100755 src/aiori-IME.c diff --git a/configure.ac b/configure.ac index 7b49a39..7042355 100755 --- a/configure.ac +++ b/configure.ac @@ -78,6 +78,17 @@ AS_IF([test "x$with_lustre" != xno], [ ]) ]) +# IME (DDN's Infinite Memory Engine) support +AC_ARG_WITH([ime], + [AS_HELP_STRING([--with-ime], + [support IO with IME backend @<:@default=no@:>@])], + [], + [with_ime=no]) +AM_CONDITIONAL([USE_IME_AIORI], [test x$with_ime = xyes]) +AM_COND_IF([USE_IME_AIORI],[ + AC_DEFINE([USE_IME_AIORI], [], [Build IME backend AIORI]) +]) + # HDF5 support AC_ARG_WITH([hdf5], [AS_HELP_STRING([--with-hdf5], diff --git a/doc/USER_GUIDE b/doc/USER_GUIDE index 8581d3e..dd02262 100755 --- a/doc/USER_GUIDE +++ b/doc/USER_GUIDE @@ -52,7 +52,7 @@ Two ways to run IOR: * 3. OPTIONS * ************** These options are to be used on the command line. E.g., 'IOR -a POSIX -b 4K'. - -a S api -- API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI] + -a S api -- API for I/O [POSIX|MPIIO|HDF5|HDFS|IME|S3|S3_EMC|NCMPI] -A N refNum -- user reference number to include in long summary -b N blockSize -- contiguous bytes to write per task (e.g.: 8, 4k, 2m, 1g) -B useO_DIRECT -- uses O_DIRECT for POSIX, bypassing I/O buffers @@ -121,8 +121,8 @@ GENERAL: * refNum - user supplied reference number, included in long summary [0] - * api - must be set to one of POSIX, MPIIO, HDF5, HDFS, S3, - S3_EMC, or NCMPI, depending on test [POSIX] + * api - must be set to one of POSIX, MPIIO, HDF5, HDFS, IME, + S3, S3_EMC, or NCMPI, depending on test [POSIX] * testFile - name of the output file [testFile] NOTE: with filePerProc set, the tasks can round @@ -396,7 +396,7 @@ various application codes. Details are included in each script as necessary. An example of a script: ===============> start script <=============== IOR START - api=[POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI] + api=[POSIX|MPIIO|HDF5|HDFS|IME|S3|S3_EMC|NCMPI] testFile=testFile hintsFileName=hintsFile repetitions=8 diff --git a/src/Makefile.am b/src/Makefile.am index 94416cd..2508802 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -42,6 +42,13 @@ extraSOURCES += aiori-HDF5.c extraLDADD += -lhdf5 -lz endif +if USE_IME_AIORI +extraSOURCES += aiori-IME.c +extraCPPFLAGS += -I/opt/ddn/ime/include +extraLDFLAGS += -L/opt/ddn/ime/lib +extraLDADD += -lim_client +endif + if USE_MPIIO_AIORI extraSOURCES += aiori-MPIIO.c endif diff --git a/src/aiori-IME.c b/src/aiori-IME.c new file mode 100755 index 0000000..70b4f0f --- /dev/null +++ b/src/aiori-IME.c @@ -0,0 +1,273 @@ +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + */ +/******************************************************************************\ +* * +* Copyright (c) 2003, The Regents of the University of California. * +* Copyright (c) 2018, DataDirect Networks. * +* See the file COPYRIGHT for a complete copyright notice and license. * +* * +******************************************************************************** +* +* Implement abstract I/O interface for DDN Infinite Memory Engine (IME). +* +\******************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include /* sys_errlist */ +#include /* IO operations */ + +#include "ior.h" +#include "iordef.h" +#include "aiori.h" +#include "utilities.h" +#include "ime_native.h" + +#ifndef O_BINARY /* Required on Windows */ +# define O_BINARY 0 +#endif + +/**************************** P R O T O T Y P E S *****************************/ + +static void *IME_Create(char *, IOR_param_t *); +static void *IME_Open(char *, IOR_param_t *); +static void IME_Close(void *, IOR_param_t *); +static void IME_Delete(char *, IOR_param_t *); +static void IME_SetVersion(IOR_param_t *); +static void IME_Fsync(void *, IOR_param_t *); +static int IME_Access(const char *, int, IOR_param_t *); +static IOR_offset_t IME_GetFileSize(IOR_param_t *, MPI_Comm, char *); +static IOR_offset_t IME_Xfer(int, void *, IOR_size_t *, + IOR_offset_t, IOR_param_t *); + +/************************** D E C L A R A T I O N S ***************************/ + +extern int rank; +extern int rankOffset; +extern int verbose; +extern MPI_Comm testComm; + +ior_aiori_t ime_aiori = { + .name = "IME", + .create = IME_Create, + .open = IME_Open, + .xfer = IME_Xfer, + .close = IME_Close, + .delete = IME_Delete, + .set_version = IME_SetVersion, + .fsync = IME_Fsync, + .get_file_size = IME_GetFileSize, + .access = IME_Access, +}; + +/***************************** F U N C T I O N S ******************************/ + +/* + * Try to access a file through the IME interface. + */ +static int IME_Access(const char *path, int mode, IOR_param_t *param) +{ + (void)param; + + return ime_native_access(path, mode); +} + +/* + * Creat and open a file through the IME interface. + */ +static void *IME_Create(char *testFileName, IOR_param_t *param) +{ + return IME_Open(testFileName, param); +} + +/* + * Open a file through the IME interface. + */ +static void *IME_Open(char *testFileName, IOR_param_t *param) +{ + int fd_oflag = O_BINARY; + int *fd; + + fd = (int *)malloc(sizeof(int)); + if (fd == NULL) + ERR("Unable to malloc file descriptor"); + + if (param->useO_DIRECT) + set_o_direct_flag(&fd_oflag); + + if (param->openFlags & IOR_RDONLY) + fd_oflag |= O_RDONLY; + if (param->openFlags & IOR_WRONLY) + fd_oflag |= O_WRONLY; + if (param->openFlags & IOR_RDWR) + fd_oflag |= O_RDWR; + if (param->openFlags & IOR_APPEND) + fd_oflag |= O_APPEND; + if (param->openFlags & IOR_CREAT) + fd_oflag |= O_CREAT; + if (param->openFlags & IOR_EXCL) + fd_oflag |= O_EXCL; + if (param->openFlags & IOR_TRUNC) + fd_oflag |= O_TRUNC; + + *fd = ime_native_open(testFileName, fd_oflag, 0664); + if (*fd < 0) { + free(fd); + ERR("cannot open file"); + } + + return((void *)fd); +} + +/* + * Write or read access to file using the IM interface. + */ +static IOR_offset_t IME_Xfer(int access, void *file, IOR_size_t *buffer, + IOR_offset_t length, IOR_param_t *param) +{ + int xferRetries = 0; + long long remaining = (long long)length; + char *ptr = (char *)buffer; + int fd = *(int *)file; + long long rc; + + while (remaining > 0) { + /* write/read file */ + if (access == WRITE) { /* WRITE */ + if (verbose >= VERBOSE_4) { + fprintf(stdout, "task %d writing to offset %lld\n", + rank, param->offset + length - remaining); + } + + rc = ime_native_pwrite(fd, ptr, remaining, param->offset); + + if (param->fsyncPerWrite) + IME_Fsync(&fd, param); + } else { /* READ or CHECK */ + if (verbose >= VERBOSE_4) { + fprintf(stdout, "task %d reading from offset %lld\n", + rank, param->offset + length - remaining); + } + + rc = ime_native_pread(fd, ptr, remaining, param->offset); + if (rc == 0) + ERR("hit EOF prematurely"); + else if (rc < 0) + ERR("read failed"); + } + + if (rc < remaining) { + fprintf(stdout, "WARNING: Task %d, partial %s, %lld of " + "%lld bytes at offset %lld\n", + rank, access == WRITE ? "write" : "read", rc, + remaining, param->offset + length - remaining ); + + if (param->singleXferAttempt) { + MPI_CHECK(MPI_Abort(MPI_COMM_WORLD, -1), + "barrier error"); + } + + if (xferRetries > MAX_RETRY) { + ERR( "too many retries -- aborting" ); + } + } else if (rc > remaining) /* this should never happen */ + ERR("too many bytes transferred!?!"); + + assert(rc >= 0); + assert(rc <= remaining); + remaining -= rc; + ptr += rc; + xferRetries++; + } + + return(length); +} + +/* + * Perform fsync(). + */ +static void IME_Fsync(void *fd, IOR_param_t *param) +{ + if (ime_native_fsync(*(int *)fd) != 0) + WARN("cannot perform fsync on file"); +} + +/* + * Close a file through the IME interface. + */ +static void IME_Close(void *fd, IOR_param_t *param) +{ + if (ime_native_close(*(int *)fd) != 0) + { + free(fd); + ERR("cannot close file"); + } + else + free(fd); +} + +/* + * Delete a file through the IME interface. + */ +static void IME_Delete(char *testFileName, IOR_param_t *param) +{ + char errmsg[256]; + sprintf(errmsg, "[RANK %03d]:cannot delete file %s\n", + rank, testFileName); + if (ime_native_unlink(testFileName) != 0) + WARN(errmsg); +} + +/* + * Determine API version. + */ +static void IME_SetVersion(IOR_param_t *test) +{ + strcpy(test->apiVersion, test->api); +} + +/* + * Use IME stat() to return aggregate file size. + */ +static IOR_offset_t IME_GetFileSize(IOR_param_t *test, MPI_Comm testComm, + char *testFileName) +{ + struct stat stat_buf; + IOR_offset_t aggFileSizeFromStat, tmpMin, tmpMax, tmpSum; + + if (ime_native_stat(testFileName, &stat_buf) != 0) { + ERR("cannot get status of written file"); + } + aggFileSizeFromStat = stat_buf.st_size; + + if (test->filePerProc) { + MPI_CHECK(MPI_Allreduce(&aggFileSizeFromStat, &tmpSum, 1, + MPI_LONG_LONG_INT, MPI_SUM, testComm), + "cannot total data moved"); + aggFileSizeFromStat = tmpSum; + } else { + MPI_CHECK(MPI_Allreduce(&aggFileSizeFromStat, &tmpMin, 1, + MPI_LONG_LONG_INT, MPI_MIN, testComm), + "cannot total data moved"); + MPI_CHECK(MPI_Allreduce(&aggFileSizeFromStat, &tmpMax, 1, + MPI_LONG_LONG_INT, MPI_MAX, testComm), + "cannot total data moved"); + + if (tmpMin != tmpMax) { + if (rank == 0) { + WARN("inconsistent file size by different tasks"); + } + /* incorrect, but now consistent across tasks */ + aggFileSizeFromStat = tmpMin; + } + } + + return(aggFileSizeFromStat); +} diff --git a/src/aiori.c b/src/aiori.c index b886487..cc3ca8d 100644 --- a/src/aiori.c +++ b/src/aiori.c @@ -38,6 +38,9 @@ ior_aiori_t *available_aiori[] = { #ifdef USE_HDFS_AIORI &hdfs_aiori, #endif +#ifdef USE_IME_AIORI + &ime_aiori, +#endif #ifdef USE_MPIIO_AIORI &mpiio_aiori, #endif diff --git a/src/aiori.h b/src/aiori.h index 0046e1f..5ddd24d 100755 --- a/src/aiori.h +++ b/src/aiori.h @@ -87,6 +87,7 @@ typedef struct ior_aiori { extern ior_aiori_t dummy_aiori; extern ior_aiori_t hdf5_aiori; extern ior_aiori_t hdfs_aiori; +extern ior_aiori_t ime_aiori; extern ior_aiori_t mpiio_aiori; extern ior_aiori_t ncmpi_aiori; extern ior_aiori_t posix_aiori; From 54fa3fa8e99668c4d6e44c94b9d4bc0b1346ed47 Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Tue, 7 Aug 2018 23:28:19 +0100 Subject: [PATCH 70/80] Fixed wrong output. --- src/ior-output.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ior-output.c b/src/ior-output.c index 27810d7..932780b 100644 --- a/src/ior-output.c +++ b/src/ior-output.c @@ -416,9 +416,9 @@ void ShowSetup(IOR_param_t *params) PrintKeyVal("apiVersion", params->apiVersion); PrintKeyVal("test filename", params->testFileName); PrintKeyVal("access", params->filePerProc ? "file-per-process" : "single-shared-file"); - PrintKeyVal("type", params->collective == FALSE ? "independent" : "collective"); + PrintKeyVal("type", params->collective ? "collective" : "independent"); PrintKeyValInt("segments", params->segmentCount); - PrintKeyVal("ordering in a file", params->randomOffset ? "sequential" : "random"); + PrintKeyVal("ordering in a file", params->randomOffset ? "random" : "sequential"); if (params->reorderTasks == FALSE && params->reorderTasksRandom == FALSE) { PrintKeyVal("ordering inter file", "no tasks offsets"); } From df34f024ba0a4b6271550bb4c92481b03f1bf0fd Mon Sep 17 00:00:00 2001 From: Jean-Yves VET Date: Wed, 8 Aug 2018 18:18:54 +0200 Subject: [PATCH 71/80] Fix warnings reported at compilation time This patch fixes most of the warnings caused by unused variables and assignments from incompatible type. --- src/aiori-MPIIO.c | 15 ++++++++------- src/ior-output.c | 6 ------ src/ior.c | 4 ---- src/ior.h | 2 +- src/mdtest.c | 18 +++++++++--------- src/parse_options.c | 1 - src/test/lib.c | 14 +++++++++++++- src/utilities.c | 2 +- 8 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/aiori-MPIIO.c b/src/aiori-MPIIO.c index d218b18..2ffdc14 100755 --- a/src/aiori-MPIIO.c +++ b/src/aiori-MPIIO.c @@ -271,10 +271,14 @@ static IOR_offset_t MPIIO_Xfer(int access, void *fd, IOR_size_t * buffer, /* point functions to appropriate MPIIO calls */ if (access == WRITE) { /* WRITE */ - Access = MPI_File_write; - Access_at = MPI_File_write_at; - Access_all = MPI_File_write_all; - Access_at_all = MPI_File_write_at_all; + Access = (int (MPIAPI *)(MPI_File, void *, int, + MPI_Datatype, MPI_Status *)) MPI_File_write; + Access_at = (int (MPIAPI *)(MPI_File, MPI_Offset, void *, int, + MPI_Datatype, MPI_Status *)) MPI_File_write_at; + Access_all = (int (MPIAPI *) (MPI_File, void *, int, + MPI_Datatype, MPI_Status *)) MPI_File_write_all; + Access_at_all = (int (MPIAPI *) (MPI_File, MPI_Offset, void *, int, + MPI_Datatype, MPI_Status *)) MPI_File_write_at_all; /* * this needs to be properly implemented: * @@ -414,9 +418,6 @@ void MPIIO_Delete(char *testFileName, IOR_param_t * param) static char* MPIIO_GetVersion() { static char ver[1024] = {}; - if (ver){ - return ver; - } int version, subversion; MPI_CHECK(MPI_Get_version(&version, &subversion), "cannot get MPI version"); sprintf(ver, "(%d.%d)", version, subversion); diff --git a/src/ior-output.c b/src/ior-output.c index 27810d7..7d7de61 100644 --- a/src/ior-output.c +++ b/src/ior-output.c @@ -52,12 +52,6 @@ static void PrintKeyValStart(char * key){ } } -static void PrintNewLine(){ - if (outputFormat == OUTPUT_DEFAULT){ - fprintf(out_resultfile, "\n"); - } -} - static void PrintNextToken(){ if(needNextToken){ needNextToken = 0; diff --git a/src/ior.c b/src/ior.c index 823afb9..db87b2e 100755 --- a/src/ior.c +++ b/src/ior.c @@ -92,7 +92,6 @@ IOR_test_t * ior_run(int argc, char **argv, MPI_Comm world_com, FILE * world_out int ior_main(int argc, char **argv) { - int i; IOR_test_t *tests_head; IOR_test_t *tptr; @@ -616,7 +615,6 @@ FillIncompressibleBuffer(void* buffer, IOR_param_t * test) lo = (unsigned long long) rand_r(&test->incompressibleSeed); buf[i] = hi | lo; } - } unsigned int reseed_incompressible_prng = TRUE; @@ -935,7 +933,6 @@ static void RemoveFile(char *testFileName, int filePerProc, IOR_param_t * test) */ static void InitTests(IOR_test_t *tests, MPI_Comm com) { - IOR_test_t *testsHead = tests; int size; MPI_CHECK(MPI_Comm_size(com, & size), "MPI_Comm_size() error"); @@ -1837,7 +1834,6 @@ static IOR_offset_t WriteOrReadSingle(IOR_offset_t pairCnt, IOR_offset_t *offset static IOR_offset_t WriteOrRead(IOR_param_t * test, IOR_results_t * results, void *fd, int access, IOR_io_buffers* ioBuffers) { int errors = 0; - IOR_offset_t amtXferred; IOR_offset_t transferCount = 0; uint64_t pairCnt = 0; IOR_offset_t *offsetArray; diff --git a/src/ior.h b/src/ior.h index 2ef7a32..43177fd 100755 --- a/src/ior.h +++ b/src/ior.h @@ -140,7 +140,7 @@ typedef struct unsigned int timeStampSignatureValue; /* value for time stamp signature */ void * fd_fppReadCheck; /* additional fd for fpp read check */ int randomSeed; /* random seed for write/read check */ - int incompressibleSeed; /* random seed for incompressible file creation */ + unsigned int incompressibleSeed; /* random seed for incompressible file creation */ int randomOffset; /* access is to random offsets */ size_t memoryPerTask; /* additional memory used per task */ size_t memoryPerNode; /* additional memory used per node */ diff --git a/src/mdtest.c b/src/mdtest.c index a9cc1ed..b646aef 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -86,7 +86,6 @@ static char testdirpath[MAX_PATHLEN]; static char base_tree_name[MAX_PATHLEN]; static char **filenames; static char hostname[MAX_PATHLEN]; -static char unique_dir[MAX_PATHLEN]; static char mk_name[MAX_PATHLEN]; static char stat_name[MAX_PATHLEN]; static char read_name[MAX_PATHLEN]; @@ -385,8 +384,6 @@ static void create_file (const char *path, uint64_t itemNum) { void create_remove_items_helper(const int dirs, const int create, const char *path, uint64_t itemNum, rank_progress_t * progress) { - char curr_item[MAX_PATHLEN]; - if (( rank == 0 ) && ( verbose >= 1 )) { fprintf( out_logfile, "V-1: Entering create_remove_items_helper...\n" ); fflush( out_logfile ); @@ -1564,9 +1561,9 @@ void summarize_results(int iterations) { /* Checks to see if the test setup is valid. If it isn't, fail. */ void valid_tests() { - if(stone_wall_timer_seconds > 0 && branch_factor > 1 || ! barriers){ - fprintf(out_logfile, "Error, stone wall timer does only work with a branch factor <= 1 and with barriers\n"); - MPI_Abort(testComm, 1); + if (((stone_wall_timer_seconds > 0) && (branch_factor > 1)) || ! barriers) { + fprintf(out_logfile, "Error, stone wall timer does only work with a branch factor <= 1 and with barriers\n"); + MPI_Abort(testComm, 1); } if (!create_only && !stat_only && !read_only && !remove_only) { @@ -1859,7 +1856,7 @@ static void mdtest_iteration(int i, int j, MPI_Group testgroup, mdtest_results_t /* start and end times of directory tree create/remove */ double startCreate, endCreate; - int k, c; + int k; if (rank == 0 && verbose >= 1) { fprintf(out_logfile, "V-1: main: * iteration %d *\n", j+1); @@ -2149,7 +2146,7 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * init_clock(); mdtest_init_args(); - int i, j, k; + int i, j; int nodeCount; MPI_Group worldgroup, testgroup; struct { @@ -2393,7 +2390,10 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * /* setup directory path to work in */ if (path_count == 0) { /* special case where no directory path provided with '-d' option */ - char * dir = getcwd(testdirpath, MAX_PATHLEN); + char *ret = getcwd(testdirpath, MAX_PATHLEN); + if (ret == NULL) { + FAIL("Unable to get current working directory"); + } path_count = 1; } else { strcpy(testdirpath, filenames[rank%path_count]); diff --git a/src/parse_options.c b/src/parse_options.c index 485f4a2..55ef5ab 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -499,7 +499,6 @@ IOR_test_t *ParseCommandLine(int argc, char **argv) LAST_OPTION, }; - int i; IOR_test_t *tests = NULL; GetPlatformName(initialTestParams.platform); diff --git a/src/test/lib.c b/src/test/lib.c index 64ebf31..55ca6f0 100644 --- a/src/test/lib.c +++ b/src/test/lib.c @@ -3,18 +3,30 @@ int main(int argc, char ** argv){ int rank; + int ret = 0; + MPI_Init(& argc, & argv); MPI_Comm_rank(MPI_COMM_WORLD, & rank); if (rank == 0){ char * param[] = {"./ior", "-a", "DUMMY"}; IOR_test_t * res = ior_run(3, param, MPI_COMM_SELF, stdout); + if (res == NULL) + { + fprintf(stderr, "Could not run ior\n"); + ret = 1; + } } if (rank == 0){ char * param[] = {"./mdtest", "-a", "DUMMY"}; mdtest_results_t * res = mdtest_run(3, param, MPI_COMM_SELF, stdout); + if (res == NULL) + { + fprintf(stderr, "Could not run mdtest\n"); + ret = 1; + } } MPI_Finalize(); - return 0; + return ret; } diff --git a/src/utilities.c b/src/utilities.c index c4a259c..7c997c1 100755 --- a/src/utilities.c +++ b/src/utilities.c @@ -136,7 +136,7 @@ void DumpBuffer(void *buffer, int CountTasksPerNode(MPI_Comm comm) { /* modern MPI provides a simple way to get the local process count */ MPI_Comm shared_comm; - int rc, count; + int count; MPI_Comm_split_type (comm, MPI_COMM_TYPE_SHARED, 0, MPI_INFO_NULL, &shared_comm); MPI_Comm_size (shared_comm, &count); From 9596b4bbe0d4cf77c4d5c5cf0ac6f8880d1218ad Mon Sep 17 00:00:00 2001 From: Jean-Yves VET Date: Tue, 7 Aug 2018 16:10:08 +0200 Subject: [PATCH 72/80] Fix compilation of aiori backends requiring external libraries This patchs makes a few adjustements in the automake file in order to properly add include and library directories. Those are required to be able to compile some aiori backends. --- src/Makefile.am | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 2508802..0e0b916 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = . +SUBDIRS = . bin_PROGRAMS = ior mdtest if USE_CAPS @@ -9,7 +9,6 @@ noinst_HEADERS = ior.h utilities.h parse_options.h aiori.h iordef.h ior-internal lib_LIBRARIES = libaiori.a libaiori_a_SOURCES = ior.c mdtest.c utilities.c parse_options.c ior-output.c option.c -libaiori_a_LIBADD = extraSOURCES = aiori.c aiori-DUMMY.c extraLDADD = @@ -83,33 +82,33 @@ extraLDADD += -laws4c extraLDADD += -laws4c_extra endif -ior_SOURCES += $(extraSOURCES) -ior_LDFLAGS += $(extraLDFLAGS) -ior_LDADD += $(extraLDADD) +ior_SOURCES += $(extraSOURCES) +ior_LDFLAGS += $(extraLDFLAGS) +ior_LDADD += $(extraLDADD) ior_CPPFLAGS += $(extraCPPFLAGS) -mdtest_SOURCES += $(extraSOURCES) -mdtest_LDFLAGS += $(extraLDFLAGS) -mdtest_LDADD += $(extraLDADD) +mdtest_SOURCES += $(extraSOURCES) +mdtest_LDFLAGS += $(extraLDFLAGS) +mdtest_LDADD += $(extraLDADD) mdtest_CPPFLAGS += $(extraCPPFLAGS) IOR_SOURCES = $(ior_SOURCES) +IOR_LDFLAGS = $(ior_LDFLAGS) IOR_LDADD = $(ior_LDADD) -IOT_CPPFLAGS = $(ior_CPPFLAGS) +IOR_CPPFLAGS = $(ior_CPPFLAGS) MDTEST_SOURCES = $(mdtest_SOURCES) +MDTEST_LDFLAGS = $(mdtest_LDFLAGS) MDTEST_LDADD = $(mdtest_LDADD) MDTEST_CPPFLAGS = $(mdtest_CPPFLAGS) libaiori_a_SOURCES += $(extraSOURCES) -#libaiori_a_LIBADD += $(extraLDFLAGS) $(extraLDADD) - +libaiori_a_CPPFLAGS = $(extraCPPFLAGS) TESTS = testlib bin_PROGRAMS += testlib -testlib_SOURCES = ./test/lib.c -testlib_LDFLAGS = -testlib_LDADD = libaiori.a $(extraLDADD) -testlib_CPPFLAGS = +testlib_SOURCES = ./test/lib.c +testlib_LDFLAGS = $(extraLDFLAGS) +testlib_LDADD = libaiori.a $(extraLDADD) From 490a912e6ef1c8e02532df4e997508f7c90f1288 Mon Sep 17 00:00:00 2001 From: Jean-Yves VET Date: Wed, 8 Aug 2018 00:04:10 +0200 Subject: [PATCH 73/80] Add support for new function calls in the IME backend This patch adds initialize, finalize and stat calls to the IME backend. statfs, mkdir and rmdir are currently not supported. This patch also fixes the IME_GetVersion call. --- src/aiori-IME.c | 108 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 12 deletions(-) diff --git a/src/aiori-IME.c b/src/aiori-IME.c index 70b4f0f..c8291b3 100755 --- a/src/aiori-IME.c +++ b/src/aiori-IME.c @@ -40,12 +40,19 @@ static void *IME_Create(char *, IOR_param_t *); static void *IME_Open(char *, IOR_param_t *); static void IME_Close(void *, IOR_param_t *); static void IME_Delete(char *, IOR_param_t *); -static void IME_SetVersion(IOR_param_t *); +static char *IME_GetVersion(); static void IME_Fsync(void *, IOR_param_t *); static int IME_Access(const char *, int, IOR_param_t *); static IOR_offset_t IME_GetFileSize(IOR_param_t *, MPI_Comm, char *); static IOR_offset_t IME_Xfer(int, void *, IOR_size_t *, IOR_offset_t, IOR_param_t *); +static int IME_StatFS(const char *, ior_aiori_statfs_t *, + IOR_param_t *); +static int IME_RmDir(const char *, IOR_param_t *); +static int IME_MkDir(const char *, mode_t, IOR_param_t *); +static int IME_Stat(const char *, struct stat *, IOR_param_t *); +static void IME_Initialize(); +static void IME_Finalize(); /************************** D E C L A R A T I O N S ***************************/ @@ -55,20 +62,42 @@ extern int verbose; extern MPI_Comm testComm; ior_aiori_t ime_aiori = { - .name = "IME", - .create = IME_Create, - .open = IME_Open, - .xfer = IME_Xfer, - .close = IME_Close, - .delete = IME_Delete, - .set_version = IME_SetVersion, - .fsync = IME_Fsync, + .name = "IME", + .create = IME_Create, + .open = IME_Open, + .xfer = IME_Xfer, + .close = IME_Close, + .delete = IME_Delete, + .get_version = IME_GetVersion, + .fsync = IME_Fsync, .get_file_size = IME_GetFileSize, - .access = IME_Access, + .access = IME_Access, + .statfs = IME_StatFS, + .rmdir = IME_RmDir, + .mkdir = IME_MkDir, + .stat = IME_Stat, + .initialize = IME_Initialize, + .finalize = IME_Finalize, }; /***************************** F U N C T I O N S ******************************/ +/* + * Initialize IME (before MPI is started). + */ +static void IME_Initialize() +{ + ime_native_init(); +} + +/* + * Finlize IME (after MPI is shutdown). + */ +static void IME_Finalize() +{ + (void)ime_native_finalize(); +} + /* * Try to access a file through the IME interface. */ @@ -228,9 +257,64 @@ static void IME_Delete(char *testFileName, IOR_param_t *param) /* * Determine API version. */ -static void IME_SetVersion(IOR_param_t *test) +static char *IME_GetVersion() { - strcpy(test->apiVersion, test->api); + static char ver[1024] = {}; +#if (IME_NATIVE_API_VERSION >= 120) + strcpy(ver, ime_native_version()); +#else + strcpy(ver, "not supported"); +#endif + return ver; +} + +/* + * XXX: statfs call is currently not exposed by IME native interface. + */ +static int IME_StatFS(const char *oid, ior_aiori_statfs_t *stat_buf, + IOR_param_t *param) +{ + (void)oid; + (void)stat_buf; + (void)param; + + WARN("statfs is currently not supported in IME backend!"); + return -1; +} + +/* + * XXX: mkdir call is currently not exposed by IME native interface. + */ +static int IME_MkDir(const char *oid, mode_t mode, IOR_param_t *param) +{ + (void)oid; + (void)mode; + (void)param; + + WARN("mkdir is currently not supported in IME backend!"); + return -1; +} + +/* + * XXX: rmdir call is curretly not exposed by IME native interface. + */ +static int IME_RmDir(const char *oid, IOR_param_t *param) +{ + (void)oid; + (void)param; + + WARN("rmdir is currently not supported in IME backend!"); + return -1; +} + +/* + * Perform stat() through the IME interface. + */ +static int IME_Stat(const char *path, struct stat *buf, IOR_param_t *param) +{ + (void)param; + + return ime_native_stat(path, buf); } /* From 0ca5f26e842e26352b52c0a16fab462fdaf2428c Mon Sep 17 00:00:00 2001 From: Shane Snyder Date: Tue, 14 Aug 2018 16:18:35 -0500 Subject: [PATCH 74/80] cleanup RADOS func names and docs --- doc/sphinx/userDoc/options.rst | 2 +- doc/sphinx/userDoc/skripts.rst | 2 +- src/aiori-RADOS.c | 6 +++--- src/mdtest.c | 4 ++-- src/parse_options.c | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/sphinx/userDoc/options.rst b/doc/sphinx/userDoc/options.rst index 7626d0e..bb7eb92 100644 --- a/doc/sphinx/userDoc/options.rst +++ b/doc/sphinx/userDoc/options.rst @@ -17,7 +17,7 @@ normal parameters override each other, so the last one executed. Command line options -------------------- These options are to be used on the command line. E.g., 'IOR -a POSIX -b 4K'. - -a S api -- API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI] + -a S api -- API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI|RADOS] -A N refNum -- user reference number to include in long summary -b N blockSize -- contiguous bytes to write per task (e.g.: 8, 4k, 2m, 1g) -B useO_DIRECT -- uses O_DIRECT for POSIX, bypassing I/O buffers diff --git a/doc/sphinx/userDoc/skripts.rst b/doc/sphinx/userDoc/skripts.rst index bfb07b2..964f1ac 100644 --- a/doc/sphinx/userDoc/skripts.rst +++ b/doc/sphinx/userDoc/skripts.rst @@ -25,7 +25,7 @@ Syntax: An example of a script: :: IOR START - api=[POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI] + api=[POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI|RADOS] testFile=testFile hintsFileName=hintsFile repetitions=8 diff --git a/src/aiori-RADOS.c b/src/aiori-RADOS.c index da27dec..430c1b2 100755 --- a/src/aiori-RADOS.c +++ b/src/aiori-RADOS.c @@ -56,7 +56,7 @@ static int RADOS_MkDir(const char *, mode_t, IOR_param_t *); static int RADOS_RmDir(const char *, IOR_param_t *); static int RADOS_Access(const char *, int, IOR_param_t *); static int RADOS_Stat(const char *, struct stat *, IOR_param_t *); -static option_help * RADIOS_options(); +static option_help * RADOS_options(); /************************** D E C L A R A T I O N S ***************************/ ior_aiori_t rados_aiori = { @@ -74,7 +74,7 @@ ior_aiori_t rados_aiori = { .rmdir = RADOS_RmDir, .access = RADOS_Access, .stat = RADOS_Stat, - .get_options = RADIOS_options, + .get_options = RADOS_options, }; #define RADOS_ERR(__err_str, __ret) do { \ @@ -83,7 +83,7 @@ ior_aiori_t rados_aiori = { } while(0) /***************************** F U N C T I O N S ******************************/ -static option_help * RADIOS_options(){ +static option_help * RADOS_options(){ return options; } diff --git a/src/mdtest.c b/src/mdtest.c index b646aef..61decb4 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -1308,7 +1308,7 @@ void print_help (void) { " [-n number_of_items] [-N stride_length] [-p seconds] [-r]\n" " [-R[seed]] [-s stride] [-S] [-t] [-T] [-u] [-v] [-a API]\n" " [-V verbosity_value] [-w number_of_bytes_to_write] [-W seconds] [-y] [-z depth] -Z\n" - "\t-a: API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI]\n" + "\t-a: API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI|RADOS]\n" "\t-b: branching factor of hierarchical directory structure\n" "\t-B: no barriers between phases\n" "\t-c: collective creates: task 0 does all creates\n" @@ -2164,7 +2164,7 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * char * path = "./out"; int randomize = 0; option_help options [] = { - {'a', NULL, "API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI]", OPTION_OPTIONAL_ARGUMENT, 's', & backend_name}, + {'a', NULL, "API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI|RADOS]", OPTION_OPTIONAL_ARGUMENT, 's', & backend_name}, {'b', NULL, "branching factor of hierarchical directory structure", OPTION_OPTIONAL_ARGUMENT, 'd', & branch_factor}, {'d', NULL, "the directory in which the tests will run", OPTION_OPTIONAL_ARGUMENT, 's', & path}, {'B', NULL, "no barriers between phases", OPTION_OPTIONAL_ARGUMENT, 'd', & no_barriers}, diff --git a/src/parse_options.c b/src/parse_options.c index 55ef5ab..d0dec17 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -438,7 +438,7 @@ IOR_test_t *ParseCommandLine(int argc, char **argv) parameters = & initialTestParams; option_help options [] = { - {'a', NULL, "API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI]", OPTION_OPTIONAL_ARGUMENT, 's', & initialTestParams.api}, + {'a', NULL, "API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI|RADOS]", OPTION_OPTIONAL_ARGUMENT, 's', & initialTestParams.api}, {'A', NULL, "refNum -- user supplied reference number to include in the summary", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.referenceNumber}, {'b', NULL, "blockSize -- contiguous bytes to write per task (e.g.: 8, 4k, 2m, 1g)", OPTION_OPTIONAL_ARGUMENT, 'l', & initialTestParams.blockSize}, {'B', NULL, "useO_DIRECT -- uses O_DIRECT for POSIX, bypassing I/O buffers", OPTION_FLAG, 'd', & initialTestParams.useO_DIRECT}, From dfc86b60b8aca58c1fe98e8296fa821cb1204f95 Mon Sep 17 00:00:00 2001 From: Shane Snyder Date: Tue, 14 Aug 2018 16:53:06 -0500 Subject: [PATCH 75/80] add RADOS backend options for pool, config file --- src/aiori-RADOS.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/aiori-RADOS.c b/src/aiori-RADOS.c index 430c1b2..ad4207d 100755 --- a/src/aiori-RADOS.c +++ b/src/aiori-RADOS.c @@ -29,15 +29,21 @@ /************************** O P T I O N S *****************************/ struct rados_options{ - char * username; + char * user; + char * conf; + char * pool; }; static struct rados_options o = { - .username = "admin", + .user = NULL, + .conf = NULL, + .pool = NULL, }; static option_help options [] = { - {'u', "username", "Username for the RADOS cluster", OPTION_REQUIRED_ARGUMENT, 's', & o.username}, + {'u', "user", "Username for the RADOS cluster", OPTION_REQUIRED_ARGUMENT, 's', & o.user}, + {'c', "conf", "Config file for the RADOS cluster", OPTION_REQUIRED_ARGUMENT, 's', & o.conf}, + {'p', "pool", "RADOS pool to use for I/O", OPTION_REQUIRED_ARGUMENT, 's', & o.pool}, LAST_OPTION }; @@ -92,13 +98,12 @@ static void RADOS_Cluster_Init(IOR_param_t * param) int ret; /* create RADOS cluster handle */ - ret = rados_create(¶m->rados_cluster, o.username); + ret = rados_create(¶m->rados_cluster, o.user); if (ret) RADOS_ERR("unable to create RADOS cluster handle", ret); /* set the handle using the Ceph config */ - /* XXX: HARDCODED RADOS CONF PATH */ - ret = rados_conf_read_file(param->rados_cluster, "/etc/ceph/ceph.conf"); + ret = rados_conf_read_file(param->rados_cluster, o.conf); if (ret) RADOS_ERR("unable to read RADOS config file", ret); @@ -108,9 +113,7 @@ static void RADOS_Cluster_Init(IOR_param_t * param) RADOS_ERR("unable to connect to the RADOS cluster", ret); /* create an io context for the pool we are operating on */ - /* XXX: HARDCODED RADOS POOL NAME */ - ret = rados_ioctx_create(param->rados_cluster, "cephfs_data", - ¶m->rados_ioctx); + ret = rados_ioctx_create(param->rados_cluster, o.pool, ¶m->rados_ioctx); if (ret) RADOS_ERR("unable to create an I/O context for the RADOS cluster", ret); From fefefa002d044098f2d48e42c27e200d1f95a97b Mon Sep 17 00:00:00 2001 From: Shane Snyder Date: Tue, 14 Aug 2018 17:08:04 -0500 Subject: [PATCH 76/80] make sure to allow fsyncs in RADOS --- src/ior.c | 3 ++- src/parse_options.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ior.c b/src/ior.c index db87b2e..1fed65e 100755 --- a/src/ior.c +++ b/src/ior.c @@ -1541,7 +1541,8 @@ static void ValidateTests(IOR_param_t * test) if (((strcasecmp(test->api, "POSIX") != 0) && (strcasecmp(test->api, "MPIIO") != 0) && (strcasecmp(test->api, "MMAP") != 0) - && (strcasecmp(test->api, "HDFS") != 0)) && test->fsync) + && (strcasecmp(test->api, "HDFS") != 0) + && (strcasecmp(test->api, "RADOS") != 0)) && test->fsync) WARN_RESET("fsync() not supported in selected backend", test, &defaults, fsync); if ((strcasecmp(test->api, "MPIIO") != 0) && test->preallocate) diff --git a/src/parse_options.c b/src/parse_options.c index d0dec17..0e46c27 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -449,7 +449,7 @@ IOR_test_t *ParseCommandLine(int argc, char **argv) {.help=" -O stoneWallingWearOut=1 -- once the stonewalling timout is over, all process finish to access the amount of data", .arg = OPTION_OPTIONAL_ARGUMENT}, {.help=" -O stoneWallingWearOutIterations=N -- stop after processing this number of iterations, needed for reading data back written with stoneWallingWearOut", .arg = OPTION_OPTIONAL_ARGUMENT}, {.help=" -O stoneWallingStatusFile=FILE -- this file keeps the number of iterations from stonewalling during write and allows to use them for read", .arg = OPTION_OPTIONAL_ARGUMENT}, - {'e', NULL, "fsync -- perform fsync/msync upon POSIX/MMAP write close", OPTION_FLAG, 'd', & initialTestParams.fsync}, + {'e', NULL, "fsync -- perform sync operation after each block write", OPTION_FLAG, 'd', & initialTestParams.fsync}, {'E', NULL, "useExistingTestFile -- do not remove test file before write access", OPTION_FLAG, 'd', & initialTestParams.useExistingTestFile}, {'f', NULL, "scriptFile -- test script name", OPTION_OPTIONAL_ARGUMENT, 's', & testscripts}, {'F', NULL, "filePerProc -- file-per-process", OPTION_FLAG, 'd', & initialTestParams.filePerProc}, @@ -491,7 +491,7 @@ IOR_test_t *ParseCommandLine(int argc, char **argv) {'W', NULL, "checkWrite -- check read after write", OPTION_FLAG, 'd', & initialTestParams.checkWrite}, {'x', NULL, "singleXferAttempt -- do not retry transfer if incomplete", OPTION_FLAG, 'd', & initialTestParams.singleXferAttempt}, {'X', NULL, "reorderTasksRandomSeed -- random seed for -Z option", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.reorderTasksRandomSeed}, - {'Y', NULL, "fsyncPerWrite -- perform fsync/msync after each POSIX/MMAP write", OPTION_FLAG, 'd', & initialTestParams.fsyncPerWrite}, + {'Y', NULL, "fsyncPerWrite -- perform sync operation after every write operation", OPTION_FLAG, 'd', & initialTestParams.fsyncPerWrite}, {'z', NULL, "randomOffset -- access is to random, not sequential, offsets within a file", OPTION_FLAG, 'd', & initialTestParams.randomOffset}, {'Z', NULL, "reorderTasksRandom -- changes task ordering to random ordering for readback", OPTION_FLAG, 'd', & initialTestParams.reorderTasksRandom}, {.help=" -O summaryFile=FILE -- store result data into this file", .arg = OPTION_OPTIONAL_ARGUMENT}, From bdf8e61dd133a32ef8aabd6855eaa67d659482f5 Mon Sep 17 00:00:00 2001 From: Shane Snyder Date: Tue, 14 Aug 2018 17:52:04 -0500 Subject: [PATCH 77/80] add notes ob how to run IOR against ceph/demo --- testing/docker/ceph/NOTES | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 testing/docker/ceph/NOTES diff --git a/testing/docker/ceph/NOTES b/testing/docker/ceph/NOTES new file mode 100644 index 0000000..2023922 --- /dev/null +++ b/testing/docker/ceph/NOTES @@ -0,0 +1,35 @@ +Following are basic notes on how to deploy the 'ceph/demo' docker container. The 'ceph/demo' container bootstraps a complete Ceph cluster with all necessary daemons already running, providing a convenient environment for evaluating the correctness of the RADOS backend for IOR, in our case. + +########################## +# Pull 'ceph/demo' image # +########################## + +Run `docker pull ceph/demo` to download the image to your system. + +################################ +# Deploy 'ceph/demo' conatiner # +################################ + +To deploy the Ceph cluster, execute the following command: + +`docker run -it --net=host -v /etc/ceph:/etc/ceph -e MON_IP=10.0.0.1 -e CEPH_PUBLIC_NETWORK=10.0.0.0/24 ceph/demo` + +The only necessary modification to the above command is to provide the correct network IP address for MON_IP and to provide the corresponding CIDR notation of this IP for CEPH_PUBLIC_NETWORK, as illustrated. + +NOTE: The above command starts the docker container in interactive mode. Replace '-it' with '-d' to run in the background as a daemon. + +############################### +# Run IOR against 'ceph/demo' # +############################### + +With a Ceph cluster now deployed, running IOR against it is straightforward: + +`./ior -a RADOS -- -u admin -c /etc/ceph/cephconf -p cephfs_data` + +All command line arguments following the '--' are required. + +-u is the Ceph username (e.g., admin) +-c is the Ceph config file (typically found in /etc/ceph/ceph.conf) +-p is the Ceph pool to perform I/O to (e.g., cephfs_data) + +NOTE: Permissions of the various config files, keyrings, etc. inside of /etc/ceph may need to be modified to be readable by the user running IOR (e.g., `sudo chmod 644 /etc/ceph/*`). These various files are created internally within the docker container and may not be readable by other users. From 4dcf36c1a440c9a3933681296656ff761346433a Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Wed, 15 Aug 2018 17:07:17 +0100 Subject: [PATCH 78/80] Help text now outputs *compiled* APIs. --- src/aiori.c | 11 +++++++++++ src/aiori.h | 1 + src/mdtest.c | 17 ++++++++++++++--- src/parse_options.c | 7 ++++++- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/aiori.c b/src/aiori.c index cc3ca8d..f5d5719 100644 --- a/src/aiori.c +++ b/src/aiori.c @@ -61,6 +61,17 @@ ior_aiori_t *available_aiori[] = { NULL }; +void aiori_supported_apis(char * APIs){ + ior_aiori_t **tmp = available_aiori; + if(*tmp != NULL){ + APIs += sprintf(APIs, "%s", (*tmp)->name); + tmp++; + for (; *tmp != NULL; ++tmp) { + APIs += sprintf(APIs, "|%s", (*tmp)->name); + } + } +} + /** * Default statfs implementation. * diff --git a/src/aiori.h b/src/aiori.h index 5ddd24d..0b0ffda 100755 --- a/src/aiori.h +++ b/src/aiori.h @@ -101,6 +101,7 @@ void aiori_initialize(); void aiori_finalize(); const ior_aiori_t *aiori_select (const char *api); int aiori_count (void); +void aiori_supported_apis(char * APIs); const char *aiori_default (void); /* some generic POSIX-based backend calls */ diff --git a/src/mdtest.c b/src/mdtest.c index 61decb4..a5e3c39 100644 --- a/src/mdtest.c +++ b/src/mdtest.c @@ -1302,13 +1302,18 @@ void file_test(const int iteration, const int ntasks, const char *path, rank_pro void print_help (void) { int j; + char APIs[1024]; + aiori_supported_apis(APIs); + char apiStr[1024]; + sprintf(apiStr, "API for I/O [%s]", APIs); + fprintf(out_logfile, "Usage: mdtest [-b branching_factor] [-B] [-c] [-C] [-d testdir] [-D] [-e number_of_bytes_to_read]\n" " [-E] [-f first] [-F] [-h] [-i iterations] [-I items_per_dir] [-l last] [-L]\n" " [-n number_of_items] [-N stride_length] [-p seconds] [-r]\n" " [-R[seed]] [-s stride] [-S] [-t] [-T] [-u] [-v] [-a API]\n" " [-V verbosity_value] [-w number_of_bytes_to_write] [-W seconds] [-y] [-z depth] -Z\n" - "\t-a: API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI|RADOS]\n" + "\t-a: %s\n" "\t-b: branching factor of hierarchical directory structure\n" "\t-B: no barriers between phases\n" "\t-c: collective creates: task 0 does all creates\n" @@ -1341,7 +1346,8 @@ void print_help (void) { "\t-x: StoneWallingStatusFile; contains the number of iterations of the creation phase, can be used to split phases across runs\n" "\t-y: sync file after writing\n" "\t-z: depth of hierarchical directory structure\n" - "\t-Z: print time instead of rate\n" + "\t-Z: print time instead of rate\n", + apiStr ); MPI_Initialized(&j); @@ -2163,8 +2169,13 @@ mdtest_results_t * mdtest_run(int argc, char **argv, MPI_Comm world_com, FILE * int no_barriers = 0; char * path = "./out"; int randomize = 0; + char APIs[1024]; + aiori_supported_apis(APIs); + char apiStr[1024]; + sprintf(apiStr, "API for I/O [%s]", APIs); + option_help options [] = { - {'a', NULL, "API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI|RADOS]", OPTION_OPTIONAL_ARGUMENT, 's', & backend_name}, + {'a', NULL, apiStr, OPTION_OPTIONAL_ARGUMENT, 's', & backend_name}, {'b', NULL, "branching factor of hierarchical directory structure", OPTION_OPTIONAL_ARGUMENT, 'd', & branch_factor}, {'d', NULL, "the directory in which the tests will run", OPTION_OPTIONAL_ARGUMENT, 's', & path}, {'B', NULL, "no barriers between phases", OPTION_OPTIONAL_ARGUMENT, 'd', & no_barriers}, diff --git a/src/parse_options.c b/src/parse_options.c index 0e46c27..80b99a3 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -437,8 +437,13 @@ IOR_test_t *ParseCommandLine(int argc, char **argv) init_IOR_Param_t(& initialTestParams); parameters = & initialTestParams; + char APIs[1024]; + aiori_supported_apis(APIs); + char apiStr[1024]; + sprintf(apiStr, "API for I/O [%s]", APIs); + option_help options [] = { - {'a', NULL, "API for I/O [POSIX|MPIIO|HDF5|HDFS|S3|S3_EMC|NCMPI|RADOS]", OPTION_OPTIONAL_ARGUMENT, 's', & initialTestParams.api}, + {'a', NULL, apiStr, OPTION_OPTIONAL_ARGUMENT, 's', & initialTestParams.api}, {'A', NULL, "refNum -- user supplied reference number to include in the summary", OPTION_OPTIONAL_ARGUMENT, 'd', & initialTestParams.referenceNumber}, {'b', NULL, "blockSize -- contiguous bytes to write per task (e.g.: 8, 4k, 2m, 1g)", OPTION_OPTIONAL_ARGUMENT, 'l', & initialTestParams.blockSize}, {'B', NULL, "useO_DIRECT -- uses O_DIRECT for POSIX, bypassing I/O buffers", OPTION_FLAG, 'd', & initialTestParams.useO_DIRECT}, From be0b840ddd77ef517ff3b1a3c457d76c39e78c9b Mon Sep 17 00:00:00 2001 From: "Julian M. Kunkel" Date: Wed, 15 Aug 2018 17:13:59 +0100 Subject: [PATCH 79/80] First attempt to describe new backend options. --- doc/USER_GUIDE | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/USER_GUIDE b/doc/USER_GUIDE index dd02262..7ea6e49 100755 --- a/doc/USER_GUIDE +++ b/doc/USER_GUIDE @@ -47,12 +47,22 @@ Two ways to run IOR: E.g., to execute: IOR -W -f script This defaults all tests in 'script' to use write data checking. + * The Command line supports to specify additional parameters for the choosen API. + For example, username and password for the storage. + Available options are listed in the help text after selecting the API when running with -h. + For example, 'IOR -a DUMMY -h' shows the supported options for the DUMMY backend. + The options for the backend must be specified at last and are separated with + two dashes '--'. Example: 'IOR -a DUMMY -- -c 1000' defines a delay for the + file creation for the plugin. Currently, it is not possible to set these + backend options using a command line script (-f option). + + ************** * 3. OPTIONS * ************** These options are to be used on the command line. E.g., 'IOR -a POSIX -b 4K'. - -a S api -- API for I/O [POSIX|MPIIO|HDF5|HDFS|IME|S3|S3_EMC|NCMPI] + -a S api -- API for I/O, e.g., POSIX -A N refNum -- user reference number to include in long summary -b N blockSize -- contiguous bytes to write per task (e.g.: 8, 4k, 2m, 1g) -B useO_DIRECT -- uses O_DIRECT for POSIX, bypassing I/O buffers From f4b03efd72dee9ecbead5a2d39baf8d0bbecdb4d Mon Sep 17 00:00:00 2001 From: Mohamad Chaarawi Date: Mon, 27 Aug 2018 17:22:38 +0000 Subject: [PATCH 80/80] Add the DAOS ior driver. Signed-off-by: Mohamad Chaarawi --- .gitignore | 9 + README_DAOS | 48 +++ configure.ac | 10 + doc/USER_GUIDE | 37 ++ src/Makefile.am | 5 + src/aiori-DAOS.c | 889 ++++++++++++++++++++++++++++++++++++++++++++ src/aiori.c | 11 +- src/aiori.h | 9 +- src/ior.c | 22 +- src/ior.h | 15 + src/list.h | 556 +++++++++++++++++++++++++++ src/mdtest-main.c | 4 +- src/parse_options.c | 36 +- 13 files changed, 1631 insertions(+), 20 deletions(-) create mode 100644 README_DAOS create mode 100644 src/aiori-DAOS.c create mode 100644 src/list.h diff --git a/.gitignore b/.gitignore index 73dd929..327640c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +tags Makefile Makefile.in aclocal.m4 @@ -9,10 +10,12 @@ config/config.sub config/depcomp config/install-sh config/missing +config/test-driver configure contrib/.deps/ contrib/Makefile contrib/Makefile.in +contrib/cbif doc/Makefile doc/Makefile.in src/.deps/ @@ -28,7 +31,13 @@ contrib/cbif.o src/*.o src/*.i src/*.s +src/*.a src/ior +src/mdtest +src/testlib +src/test/.deps/ +src/test/.dirstamp +src/test/lib.o doc/doxygen/build doc/sphinx/_*/ diff --git a/README_DAOS b/README_DAOS new file mode 100644 index 0000000..ba4fc3e --- /dev/null +++ b/README_DAOS @@ -0,0 +1,48 @@ +Building with DAOS API +---------------------- + +At step 1 above, one must specify "--with-daos". If the DAOS +headers and libraries are not installed at respective system +default locations, then one may also needs to set CPPFLAGS and +LDFLAGS accordingly. + +Running with DAOS API +--------------------- + +One must specify an existing pool using "-O +daospool=". IOR must be launched in a way that +attaches the IOR process group to the DAOS server process group. + +One must also specify a container UUID using "-o +". If the "-E" option is given, then this UUID +shall denote an existing container created by a "matching" IOR +run. Otherwise, IOR will create a new container with this UUID. +In the latter case, one may use uuidgen(1) to generate the UUID +of the new container. + +When benchmarking write performance, one likely do not want +"-W", which causes the write phase to do one additional memory +copy for every I/O. This is due to IOR's assumption that when a +DAOS_Xfer() call returns the buffer may be released. Therefore, +random data is written when "-W" is absent, while data is copied +from IOR buffers when "-W" is present. + +See doc/USER_GUIDE for all options and directives. Note that not +all combinations of options are supported. + +Examples that should work include: + + - "ior -a DAOS -w -W -o -O + daospool=,daospoolsvc=" writes into a new container + and verifies the data, using default daosRecordSize, transferSize, + daosStripeSize, blockSize, daosAios, etc. + + - "ior -a DAOS -w -W -r -R -o -b 1g -t 4m -C -O + daospool=,daospoolsvc=,daosrecordsize=1m, + daosstripesize=4m, daosstripecount=256,daosaios=8" does all IOR tests and + shifts ranks during checkWrite and checkRead. + + - "ior -a DAOS -w -r -o -b 8g -t 1m -C -O + daospool=,daospoolsvc=,daosrecordsize=1m,daosstripesize=4m, + daosstripecount=256,daosaios=8" may be a base to be tuned for performance + benchmarking. diff --git a/configure.ac b/configure.ac index 7042355..bb643d9 100755 --- a/configure.ac +++ b/configure.ac @@ -166,6 +166,16 @@ AM_COND_IF([USE_RADOS_AIORI],[ AC_DEFINE([USE_RADOS_AIORI], [], [Build RADOS backend AIORI]) ]) +# DAOS support +AC_ARG_WITH([daos], + [AS_HELP_STRING([--with-daos], + [support IO with DAOS backend @<:@default=no@:>@])], + [], + [with_daos=no]) +AM_CONDITIONAL([USE_DAOS_AIORI], [test x$with_daos = xyes]) +AM_COND_IF([USE_DAOS_AIORI],[ + AC_DEFINE([USE_DAOS_AIORI], [], [Build DAOS backend AIORI]) +]) # aws4c is needed for the S3 backend (see --with-S3, below). diff --git a/doc/USER_GUIDE b/doc/USER_GUIDE index 7ea6e49..d76266d 100755 --- a/doc/USER_GUIDE +++ b/doc/USER_GUIDE @@ -367,6 +367,43 @@ BeeGFS-SPECIFIC (POSIX only): * beegfsChunkSize - set the striping chunk size. Must be a power of two, and greater than 64kiB, (e.g.: 256k, 1M, ...) +DAOS-ONLY: +========== + * daosGroup - group name [NULL] + + * daosPool - UUID of the pool [] + + * daosPoolSvc - pool service replica ranks (e.g., 1:2:3:4:5) [] + + * daosRecordSize - size (in bytes) of an akey record [256k] + NOTE: must divide transferSize + + * daosStripeSize - size (in bytes) of a chunk in a stripe [512k] + NOTE: must be a multiple of transferSize + + * daosStripeCount - number of stripes [64 * number of targets] + NOTE: i.e., number of dkeys + + * daosStripeMax - max length of each stripe [0] + NOTE: must be a multiple of daosStripeSize + NOTE: for write testing with small storage + NOTE: offsets in a stripe larger than daosStripeMax + are mapped to offset % daosStripeMax + + * daosAios - max number of asychonous I/Os [1] + + * daosWriteOnly - skip flushing and committing [0=FALSE] + + * daosEpoch - epoch to read or write [0] + NOTE: 0 denotes reading GHCE or writing GHCE + 1 + + * daosWait - epoch to wait when opening the container [0] + + * daosKill - kill a target in the middle of the test [0] + NOTE: must also specify daosObjectClass=repl + + * daosObjectClass - object class (tiny, small, large, repl, repl_max) + [large] *********************** * 5. VERBOSITY LEVELS * diff --git a/src/Makefile.am b/src/Makefile.am index 0e0b916..32db201 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -70,6 +70,11 @@ extraSOURCES += aiori-RADOS.c extraLDADD += -lrados endif +if USE_DAOS_AIORI +extraSOURCES += aiori-DAOS.c list.h +extraLDADD += -ldaos -ldaos_common -luuid +endif + if USE_S3_AIORI extraSOURCES += aiori-S3.c if AWS4C_DIR diff --git a/src/aiori-DAOS.c b/src/aiori-DAOS.c new file mode 100644 index 0000000..9175a5a --- /dev/null +++ b/src/aiori-DAOS.c @@ -0,0 +1,889 @@ +/* + * -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + */ +/* + * SPECIAL LICENSE RIGHTS-OPEN SOURCE SOFTWARE + * The Government's rights to use, modify, reproduce, release, perform, display, + * or disclose this software are subject to the terms of Contract No. B599860, + * and the terms of the GNU General Public License version 2. + * Any reproduction of computer software, computer software documentation, or + * portions thereof marked with this legend must also reproduce the markings. + */ +/* + * Copyright (c) 2013, 2016 Intel Corporation. + */ +/* + * This file implements the abstract I/O interface for DAOS. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ior.h" +#include "aiori.h" +#include "iordef.h" +#include "list.h" + +/**************************** P R O T O T Y P E S *****************************/ + +static void DAOS_Init(IOR_param_t *); +static void DAOS_Fini(IOR_param_t *); +static void *DAOS_Create(char *, IOR_param_t *); +static void *DAOS_Open(char *, IOR_param_t *); +static IOR_offset_t DAOS_Xfer(int, void *, IOR_size_t *, + IOR_offset_t, IOR_param_t *); +static void DAOS_Close(void *, IOR_param_t *); +static void DAOS_Delete(char *, IOR_param_t *); +static char* DAOS_GetVersion(); +static void DAOS_Fsync(void *, IOR_param_t *); +static IOR_offset_t DAOS_GetFileSize(IOR_param_t *, MPI_Comm, char *); + +/************************** D E C L A R A T I O N S ***************************/ + +ior_aiori_t daos_aiori = { + .name = "DAOS", + .create = DAOS_Create, + .open = DAOS_Open, + .xfer = DAOS_Xfer, + .close = DAOS_Close, + .delete = DAOS_Delete, + .get_version = DAOS_GetVersion, + .fsync = DAOS_Fsync, + .get_file_size = DAOS_GetFileSize, + .initialize = DAOS_Init, + .finalize = DAOS_Fini, +}; + +enum handleType { + POOL_HANDLE, + CONTAINER_HANDLE +}; + +struct fileDescriptor { + daos_handle_t container; + daos_cont_info_t containerInfo; + daos_handle_t object; + daos_epoch_t epoch; +}; + +struct aio { + cfs_list_t a_list; + char a_dkeyBuf[32]; + daos_key_t a_dkey; + daos_recx_t a_recx; + unsigned char a_csumBuf[32]; + daos_csum_buf_t a_csum; + daos_epoch_range_t a_epochRange; + daos_iod_t a_iod; + daos_iov_t a_iov; + daos_sg_list_t a_sgl; + struct daos_event a_event; +}; + +static daos_handle_t eventQueue; +static struct daos_event **events; +static unsigned char *buffers; +static int nAios; +static daos_handle_t pool; +static daos_pool_info_t poolInfo; +static daos_oclass_id_t objectClass = DAOS_OC_LARGE_RW; +static CFS_LIST_HEAD(aios); +static IOR_offset_t total_size; + +/***************************** F U N C T I O N S ******************************/ + +/* For DAOS methods. */ +#define DCHECK(rc, format, ...) \ +do { \ + int _rc = (rc); \ + \ + if (_rc < 0) { \ + fprintf(stdout, "ior ERROR (%s:%d): %d: %d: " \ + format"\n", __FILE__, __LINE__, rank, _rc, \ + ##__VA_ARGS__); \ + fflush(stdout); \ + MPI_Abort(MPI_COMM_WORLD, -1); \ + } \ +} while (0) + +#define INFO(level, param, format, ...) \ +do { \ + if (param->verbose >= level) \ + printf("[%d] "format"\n", rank, ##__VA_ARGS__); \ +} while (0) + +/* For generic errors like invalid command line options. */ +#define GERR(format, ...) \ +do { \ + fprintf(stdout, format"\n", ##__VA_ARGS__); \ + MPI_CHECK(MPI_Abort(MPI_COMM_WORLD, -1), "MPI_Abort() error"); \ +} while (0) + +/* Distribute process 0's pool or container handle to others. */ +static void HandleDistribute(daos_handle_t *handle, enum handleType type, + IOR_param_t *param) +{ + daos_iov_t global; + int rc; + + assert(type == POOL_HANDLE || !daos_handle_is_inval(pool)); + + global.iov_buf = NULL; + global.iov_buf_len = 0; + global.iov_len = 0; + + if (rank == 0) { + /* Get the global handle size. */ + if (type == POOL_HANDLE) + rc = daos_pool_local2global(*handle, &global); + else + rc = daos_cont_local2global(*handle, &global); + DCHECK(rc, "Failed to get global handle size"); + } + + MPI_CHECK(MPI_Bcast(&global.iov_buf_len, 1, MPI_UINT64_T, 0, + param->testComm), + "Failed to bcast global handle buffer size"); + + global.iov_buf = malloc(global.iov_buf_len); + if (global.iov_buf == NULL) + ERR("Failed to allocate global handle buffer"); + + if (rank == 0) { + if (type == POOL_HANDLE) + rc = daos_pool_local2global(*handle, &global); + else + rc = daos_cont_local2global(*handle, &global); + DCHECK(rc, "Failed to create global handle"); + } + + MPI_CHECK(MPI_Bcast(global.iov_buf, global.iov_buf_len, MPI_BYTE, 0, + param->testComm), + "Failed to bcast global pool handle"); + + if (rank != 0) { + /* A larger-than-actual length works just fine. */ + global.iov_len = global.iov_buf_len; + + if (type == POOL_HANDLE) + rc = daos_pool_global2local(global, handle); + else + rc = daos_cont_global2local(pool, global, handle); + DCHECK(rc, "Failed to get local handle"); + } + + free(global.iov_buf); +} + +static void ContainerOpen(char *testFileName, IOR_param_t *param, + daos_handle_t *container, daos_cont_info_t *info) +{ + int rc; + + if (rank == 0) { + uuid_t uuid; + unsigned int dFlags; + + rc = uuid_parse(testFileName, uuid); + DCHECK(rc, "Failed to parse 'testFile': %s", testFileName); + + if (param->open == WRITE && + param->useExistingTestFile == FALSE) { + INFO(VERBOSE_2, param, "Creating container %s", + testFileName); + + rc = daos_cont_create(pool, uuid, NULL /* ev */); + DCHECK(rc, "Failed to create container %s", + testFileName); + } + + INFO(VERBOSE_2, param, "Openning container %s", testFileName); + + if (param->open == WRITE) + dFlags = DAOS_COO_RW; + else + dFlags = DAOS_COO_RO; + + rc = daos_cont_open(pool, uuid, dFlags, container, info, + NULL /* ev */); + DCHECK(rc, "Failed to open container %s", testFileName); + + INFO(VERBOSE_2, param, "Container epoch state:"); + INFO(VERBOSE_2, param, " HCE: %lu", + info->ci_epoch_state.es_hce); + INFO(VERBOSE_2, param, " LRE: %lu", + info->ci_epoch_state.es_lre); + INFO(VERBOSE_2, param, " LHE: %lu (%lx)", + info->ci_epoch_state.es_lhe, info->ci_epoch_state.es_lhe); + INFO(VERBOSE_2, param, " GHCE: %lu", + info->ci_epoch_state.es_ghce); + INFO(VERBOSE_2, param, " GLRE: %lu", + info->ci_epoch_state.es_glre); + INFO(VERBOSE_2, param, " GHPCE: %lu", + info->ci_epoch_state.es_ghpce); + +#if 0 + if (param->open != WRITE && param->daosWait != 0) { + daos_epoch_t e; + + e = param->daosWait; + + INFO(VERBOSE_2, param, "Waiting for epoch %lu", e); + + rc = daos_epoch_wait(*container, &e, + NULL /* ignore HLE */, + NULL /* synchronous */); + DCHECK(rc, "Failed to wait for epoch %lu", + param->daosWait); + } + + if (param->open == WRITE && + param->useExistingTestFile == FALSE) { + daos_oclass_attr_t attr = { + .ca_schema = DAOS_OS_STRIPED, + .ca_resil_degree = 0, + .ca_resil = DAOS_RES_REPL, + .ca_grp_nr = 4, + .u.repl = { + .r_method = 0, + .r_num = 2 + } + }; + + INFO(VERBOSE_2, param, "Registering object class"); + + rc = daos_oclass_register(container, objectClass, &attr, + NULL /* ev */); + DCHECK(rc, "Failed to register object class"); + } +#endif + } + + HandleDistribute(container, CONTAINER_HANDLE, param); + + MPI_CHECK(MPI_Bcast(info, sizeof *info, MPI_BYTE, 0, param->testComm), + "Failed to broadcast container info"); +} + +static void ContainerClose(daos_handle_t container, IOR_param_t *param) +{ + int rc; + + if (rank != 0) { + rc = daos_cont_close(container, NULL /* ev */); + DCHECK(rc, "Failed to close container"); + } + + /* An MPI_Gather() call would probably be more efficient. */ + MPI_CHECK(MPI_Barrier(param->testComm), + "Failed to synchronize processes"); + + if (rank == 0) { + rc = daos_cont_close(container, NULL /* ev */); + DCHECK(rc, "Failed to close container"); + } +} + +static void ObjectOpen(daos_handle_t container, daos_handle_t *object, + daos_epoch_t epoch, IOR_param_t *param) +{ + daos_obj_id_t oid; + unsigned int flags; + int rc; + + oid.hi = 0; + oid.lo = 1; + daos_obj_id_generate(&oid, 0, objectClass); + +#if 0 + /** declaring object not implemented commenting it */ + if (rank == 0 && param->open == WRITE && + param->useExistingTestFile == FALSE) { + INFO(VERBOSE_2, param, "Declaring object"); + + rc = daos_obj_declare(container, oid, epoch, NULL /* oa */, + NULL /* ev */); + DCHECK(rc, "Failed to declare object"); + } +#endif + /* An MPI_Bcast() call would probably be more efficient. */ + MPI_CHECK(MPI_Barrier(param->testComm), + "Failed to synchronize processes"); + + if (param->open == WRITE) + flags = DAOS_OO_RW; + else + flags = DAOS_OO_RO; + + rc = daos_obj_open(container, oid, epoch, flags, object, NULL /* ev */); + DCHECK(rc, "Failed to open object"); +} + +static void ObjectClose(daos_handle_t object) +{ + int rc; + + rc = daos_obj_close(object, NULL /* ev */); + DCHECK(rc, "Failed to close object"); +} + +static void AIOInit(IOR_param_t *param) +{ + struct aio *aio; + int i; + int rc; + + rc = posix_memalign((void **) &buffers, sysconf(_SC_PAGESIZE), + param->transferSize * param->daosAios); + DCHECK(rc, "Failed to allocate buffer array"); + + for (i = 0; i < param->daosAios; i++) { + aio = malloc(sizeof *aio); + if (aio == NULL) + ERR("Failed to allocate aio array"); + + memset(aio, 0, sizeof *aio); + + aio->a_dkey.iov_buf = aio->a_dkeyBuf; + aio->a_dkey.iov_buf_len = sizeof aio->a_dkeyBuf; + + aio->a_recx.rx_nr = 1; + + aio->a_csum.cs_csum = &aio->a_csumBuf; + aio->a_csum.cs_buf_len = sizeof aio->a_csumBuf; + aio->a_csum.cs_len = aio->a_csum.cs_buf_len; + + aio->a_epochRange.epr_hi = DAOS_EPOCH_MAX; + + aio->a_iod.iod_name.iov_buf = "data"; + aio->a_iod.iod_name.iov_buf_len = + strlen(aio->a_iod.iod_name.iov_buf) + 1; + aio->a_iod.iod_name.iov_len = aio->a_iod.iod_name.iov_buf_len; + aio->a_iod.iod_nr = 1; + aio->a_iod.iod_type = DAOS_IOD_ARRAY; + aio->a_iod.iod_recxs = &aio->a_recx; + aio->a_iod.iod_csums = &aio->a_csum; + aio->a_iod.iod_eprs = &aio->a_epochRange; + aio->a_iod.iod_size = param->transferSize; + + aio->a_iov.iov_buf = buffers + param->transferSize * i; + aio->a_iov.iov_buf_len = param->transferSize; + aio->a_iov.iov_len = aio->a_iov.iov_buf_len; + + aio->a_sgl.sg_nr = 1; + aio->a_sgl.sg_iovs = &aio->a_iov; + + rc = daos_event_init(&aio->a_event, eventQueue, + NULL /* parent */); + DCHECK(rc, "Failed to initialize event for aio[%d]", i); + + cfs_list_add(&aio->a_list, &aios); + + INFO(VERBOSE_3, param, "Allocated AIO %p: buffer %p", aio, + aio->a_iov.iov_buf); + } + + nAios = param->daosAios; + + events = malloc((sizeof *events) * param->daosAios); + if (events == NULL) + ERR("Failed to allocate events array"); +} + +static void AIOFini(IOR_param_t *param) +{ + struct aio *aio; + struct aio *tmp; + + free(events); + + cfs_list_for_each_entry_safe(aio, tmp, &aios, a_list) { + INFO(VERBOSE_3, param, "Freeing AIO %p: buffer %p", aio, + aio->a_iov.iov_buf); + cfs_list_del_init(&aio->a_list); + daos_event_fini(&aio->a_event); + free(aio); + } + + free(buffers); +} + +static void AIOWait(IOR_param_t *param) +{ + struct aio *aio; + int i; + int rc; + + rc = daos_eq_poll(eventQueue, 0, DAOS_EQ_WAIT, param->daosAios, + events); + DCHECK(rc, "Failed to poll event queue"); + assert(rc <= param->daosAios - nAios); + + for (i = 0; i < rc; i++) { + int ret; + + aio = (struct aio *) + ((char *) events[i] - + (char *) (&((struct aio *) 0)->a_event)); + + DCHECK(aio->a_event.ev_error, "Failed to transfer (%lu, %lu)", + aio->a_iod.iod_recxs->rx_idx, + aio->a_iod.iod_recxs->rx_nr); + + daos_event_fini(&aio->a_event); + ret = daos_event_init(&aio->a_event, eventQueue, + NULL /* parent */); + DCHECK(ret, "Failed to reinitialize event for AIO %p", aio); + + cfs_list_move(&aio->a_list, &aios); + nAios++; + + if (param->verbose >= VERBOSE_3) + INFO(VERBOSE_3, param, "Completed AIO %p: buffer %p", aio, + aio->a_iov.iov_buf); + } + + INFO(VERBOSE_3, param, "Found %d completed AIOs (%d free %d busy)", rc, + nAios, param->daosAios - nAios); +} + +static void ObjectClassParse(const char *string) +{ + if (strcasecmp(string, "tiny") == 0) + objectClass = DAOS_OC_TINY_RW; + else if (strcasecmp(string, "small") == 0) + objectClass = DAOS_OC_SMALL_RW; + else if (strcasecmp(string, "large") == 0) + objectClass = DAOS_OC_LARGE_RW; + else if (strcasecmp(string, "echo") == 0) + objectClass = DAOS_OC_ECHO_RW; + else if (strcasecmp(string, "R2") == 0) + objectClass = DAOS_OC_R2_RW; + else if (strcasecmp(string, "R2S") == 0) + objectClass = DAOS_OC_R2S_RW; + else if (strcasecmp(string, "R3S") == 0) + objectClass = DAOS_OC_R3S_RW; + else if (strcasecmp(string, "R3") == 0) + objectClass = DAOS_OC_R3_RW; + else if (strcasecmp(string, "R4") == 0) + objectClass = DAOS_OC_R4_RW; + else if (strcasecmp(string, "R4S") == 0) + objectClass = DAOS_OC_R4S_RW; + else if (strcasecmp(string, "repl_max") == 0) + objectClass = DAOS_OC_REPL_MAX_RW; + else + GERR("Invalid 'daosObjectClass' argument: '%s'", string); +} + +static const char *GetGroup(IOR_param_t *param) +{ + if (strlen(param->daosGroup) == 0) + return NULL; + return param->daosGroup; +} + +static void ParseService(IOR_param_t *param, int max, d_rank_list_t *ranks) +{ + char *s; + + s = strdup(param->daosPoolSvc); + if (s == NULL) + GERR("failed to duplicate argument"); + ranks->rl_nr = 0; + while ((s = strtok(s, ":")) != NULL) { + if (ranks->rl_nr >= max) { + free(s); + GERR("at most %d pool service replicas supported", max); + } + ranks->rl_ranks[ranks->rl_nr] = atoi(s); + ranks->rl_nr++; + s = NULL; + } + free(s); +} + +static void DAOS_Init(IOR_param_t *param) +{ + int rc; + + if (strlen(param->daosObjectClass) != 0) + ObjectClassParse(param->daosObjectClass); + + if (param->filePerProc) + GERR("'filePerProc' not yet supported"); + if (param->daosStripeMax % param->daosStripeSize != 0) + GERR("'daosStripeMax' must be a multiple of 'daosStripeSize'"); + if (param->daosStripeSize % param->transferSize != 0) + GERR("'daosStripeSize' must be a multiple of 'transferSize'"); + if (param->transferSize % param->daosRecordSize != 0) + GERR("'transferSize' must be a multiple of 'daosRecordSize'"); + if (param->daosKill && ((objectClass != DAOS_OC_R2_RW) || + (objectClass != DAOS_OC_R3_RW) || + (objectClass != DAOS_OC_R4_RW) || + (objectClass != DAOS_OC_R2S_RW) || + (objectClass != DAOS_OC_R3S_RW) || + (objectClass != DAOS_OC_R4S_RW) || + (objectClass != DAOS_OC_REPL_MAX_RW))) + GERR("'daosKill' only makes sense with 'daosObjectClass=repl'"); + + if (rank == 0) + INFO(VERBOSE_0, param, "WARNING: USING daosStripeMax CAUSES READS TO RETURN INVALID DATA"); + + rc = daos_init(); + DCHECK(rc, "Failed to initialize daos"); + + rc = daos_eq_create(&eventQueue); + DCHECK(rc, "Failed to create event queue"); + + if (rank == 0) { + uuid_t uuid; + d_rank_t rank[13]; + d_rank_list_t ranks; + + if (strlen(param->daosPool) == 0) + GERR("'daosPool' must be specified"); + if (strlen(param->daosPoolSvc) == 0) + GERR("'daosPoolSvc' must be specified"); + + INFO(VERBOSE_2, param, "Connecting to pool %s %s", + param->daosPool, param->daosPoolSvc); + + rc = uuid_parse(param->daosPool, uuid); + DCHECK(rc, "Failed to parse 'daosPool': %s", param->daosPool); + ranks.rl_ranks = rank; + ParseService(param, sizeof(rank) / sizeof(rank[0]), &ranks); + + rc = daos_pool_connect(uuid, GetGroup(param), &ranks, + DAOS_PC_RW, &pool, &poolInfo, + NULL /* ev */); + DCHECK(rc, "Failed to connect to pool %s", param->daosPool); + } + + HandleDistribute(&pool, POOL_HANDLE, param); + + MPI_CHECK(MPI_Bcast(&poolInfo, sizeof poolInfo, MPI_BYTE, 0, + param->testComm), + "Failed to bcast pool info"); + + if (param->daosStripeCount == -1) + param->daosStripeCount = poolInfo.pi_ntargets * 64UL; +} + +static void DAOS_Fini(IOR_param_t *param) +{ + int rc; + + rc = daos_pool_disconnect(pool, NULL /* ev */); + DCHECK(rc, "Failed to disconnect from pool %s", param->daosPool); + + rc = daos_eq_destroy(eventQueue, 0 /* flags */); + DCHECK(rc, "Failed to destroy event queue"); + + rc = daos_fini(); + DCHECK(rc, "Failed to finalize daos"); +} + +static void *DAOS_Create(char *testFileName, IOR_param_t *param) +{ + return DAOS_Open(testFileName, param); +} + +static void *DAOS_Open(char *testFileName, IOR_param_t *param) +{ + struct fileDescriptor *fd; + daos_epoch_t ghce; + + fd = malloc(sizeof *fd); + if (fd == NULL) + ERR("Failed to allocate fd"); + + ContainerOpen(testFileName, param, &fd->container, &fd->containerInfo); + + ghce = fd->containerInfo.ci_epoch_state.es_ghce; + if (param->open == WRITE) { + if (param->daosEpoch == 0) + fd->epoch = ghce + 1; + else if (param->daosEpoch <= ghce) + GERR("Can't modify committed epoch\n"); + else + fd->epoch = param->daosEpoch; + } else { + if (param->daosEpoch == 0) { + if (param->daosWait == 0) + fd->epoch = ghce; + else + fd->epoch = param->daosWait; + } else if (param->daosEpoch > ghce) { + GERR("Can't read uncommitted epoch\n"); + } else { + fd->epoch = param->daosEpoch; + } + } + + if (rank == 0) + INFO(VERBOSE_2, param, "Accessing epoch %lu", fd->epoch); + + if (rank == 0 && param->open == WRITE) { + daos_epoch_t e = fd->epoch; + int rc; + + INFO(VERBOSE_2, param, "Holding epoch %lu", fd->epoch); + + rc = daos_epoch_hold(fd->container, &fd->epoch, + NULL /* state */, NULL /* ev */); + DCHECK(rc, "Failed to hold epoch"); + assert(fd->epoch == e); + } + + ObjectOpen(fd->container, &fd->object, fd->epoch, param); + + AIOInit(param); + + return fd; +} + +static void +kill_daos_server(IOR_param_t *param) +{ + daos_pool_info_t info; + d_rank_t rank, svc_ranks[13]; + d_rank_list_t svc, targets; + uuid_t uuid; + char *s; + int rc; + + rc = daos_pool_query(pool, NULL, &info, NULL); + DCHECK(rc, "Error in querying pool\n"); + + if (info.pi_ntargets - info.pi_ndisabled <= 1) + return; + /* choose the last alive one */ + rank = info.pi_ntargets - 1 - info.pi_ndisabled; + + rc = uuid_parse(param->daosPool, uuid); + DCHECK(rc, "Failed to parse 'daosPool': %s", param->daosPool); + + if (rc != 0) + printf("Killing tgt rank: %d (total of %d of %d already disabled)\n", + rank, info.pi_ndisabled, info.pi_ntargets); + fflush(stdout); + + rc = daos_mgmt_svc_rip(GetGroup(param), rank, true, NULL); + DCHECK(rc, "Error in killing server\n"); + + targets.rl_nr = 1; + targets.rl_ranks = &rank; + + svc.rl_ranks = svc_ranks; + ParseService(param, sizeof(svc_ranks)/ sizeof(svc_ranks[0]), &svc); + + rc = daos_pool_exclude(uuid, NULL, &svc, &targets, NULL); + DCHECK(rc, "Error in excluding pool from poolmap\n"); + + rc = daos_pool_query(pool, NULL, &info, NULL); + DCHECK(rc, "Error in querying pool\n"); + + printf("%d targets succesfully disabled\n", + info.pi_ndisabled); + +} + +static void +kill_and_sync(IOR_param_t *param) +{ + double start, end; + + start = MPI_Wtime(); + if (rank == 0) + kill_daos_server(param); + + if (rank == 0) + printf("Done killing and excluding\n"); + + MPI_CHECK(MPI_Barrier(param->testComm), + "Failed to synchronize processes"); + + end = MPI_Wtime(); + if (rank == 0) + printf("Time spent inducing failure: %lf\n", (end - start)); +} + +static IOR_offset_t DAOS_Xfer(int access, void *file, IOR_size_t *buffer, + IOR_offset_t length, IOR_param_t *param) +{ + struct fileDescriptor *fd = file; + struct aio *aio; + uint64_t stripe; + IOR_offset_t stripeOffset; + uint64_t round; + int rc; + + assert(length == param->transferSize); + assert(param->offset % length == 0); + + /** + * Currently killing only during writes + * Kills once when 1/2 of blocksize is + * written + **/ + total_size += length; + if (param->daosKill && (access == WRITE) && + ((param->blockSize)/2) == total_size) { + /** More than half written lets kill */ + if (rank == 0) + printf("Killing and Syncing\n", rank); + kill_and_sync(param); + param->daosKill = 0; + } + + /* + * Find an available AIO descriptor. If none, wait for one. + */ + while (nAios == 0) + AIOWait(param); + aio = cfs_list_entry(aios.next, struct aio, a_list); + cfs_list_move_tail(&aio->a_list, &aios); + nAios--; + + stripe = (param->offset / param->daosStripeSize) % + param->daosStripeCount; + rc = snprintf(aio->a_dkeyBuf, sizeof aio->a_dkeyBuf, "%lu", stripe); + assert(rc < sizeof aio->a_dkeyBuf); + aio->a_dkey.iov_len = strlen(aio->a_dkeyBuf) + 1; + round = param->offset / (param->daosStripeSize * param->daosStripeCount); + stripeOffset = param->daosStripeSize * round + + param->offset % param->daosStripeSize; + if (param->daosStripeMax != 0) + stripeOffset %= param->daosStripeMax; + aio->a_recx.rx_idx = stripeOffset / param->daosRecordSize; + aio->a_epochRange.epr_lo = fd->epoch; + + /* + * If the data written will be checked later, we have to copy in valid + * data instead of writing random bytes. If the data being read is for + * checking purposes, poison the buffer first. + */ + if (access == WRITE && param->checkWrite) + memcpy(aio->a_iov.iov_buf, buffer, length); + else if (access == WRITECHECK || access == READCHECK) + memset(aio->a_iov.iov_buf, '#', length); + + INFO(VERBOSE_3, param, "Starting AIO %p (%d free %d busy): access %d " + "dkey '%s' iod <%llu, %llu> sgl <%p, %lu>", aio, nAios, + param->daosAios - nAios, access, (char *) aio->a_dkey.iov_buf, + (unsigned long long) aio->a_iod.iod_recxs->rx_idx, + (unsigned long long) aio->a_iod.iod_recxs->rx_nr, + aio->a_sgl.sg_iovs->iov_buf, + (unsigned long long) aio->a_sgl.sg_iovs->iov_buf_len); + + if (access == WRITE) { + rc = daos_obj_update(fd->object, fd->epoch, &aio->a_dkey, + 1 /* nr */, &aio->a_iod, &aio->a_sgl, + &aio->a_event); + DCHECK(rc, "Failed to start update operation"); + } else { + rc = daos_obj_fetch(fd->object, fd->epoch, &aio->a_dkey, + 1 /* nr */, &aio->a_iod, &aio->a_sgl, + NULL /* maps */, &aio->a_event); + DCHECK(rc, "Failed to start fetch operation"); + } + + /* + * If this is a WRITECHECK or READCHECK, we are expected to fill data + * into the buffer before returning. Note that if this is a READ, we + * don't have to return valid data as WriteOrRead() doesn't care. + */ + if (access == WRITECHECK || access == READCHECK) { + while (param->daosAios - nAios > 0) + AIOWait(param); + memcpy(buffer, aio->a_sgl.sg_iovs->iov_buf, length); + } + + return length; +} + +static void DAOS_Close(void *file, IOR_param_t *param) +{ + struct fileDescriptor *fd = file; + int rc; + + while (param->daosAios - nAios > 0) + AIOWait(param); + AIOFini(param); + + ObjectClose(fd->object); + + if (param->open == WRITE && !param->daosWriteOnly) { + /* Wait for everybody for to complete the writes. */ + MPI_CHECK(MPI_Barrier(param->testComm), + "Failed to synchronize processes"); + + if (rank == 0) { + INFO(VERBOSE_2, param, "Flushing epoch %lu", fd->epoch); + + rc = daos_epoch_flush(fd->container, fd->epoch, + NULL /* state */, NULL /* ev */); + DCHECK(rc, "Failed to flush epoch"); + + INFO(VERBOSE_2, param, "Committing epoch %lu", + fd->epoch); + + rc = daos_epoch_commit(fd->container, fd->epoch, + NULL /* state */, NULL /* ev */); + DCHECK(rc, "Failed to commit object write"); + } + } + + ContainerClose(fd->container, param); + + free(fd); +} + +static void DAOS_Delete(char *testFileName, IOR_param_t *param) +{ + uuid_t uuid; + int rc; + + INFO(VERBOSE_2, param, "Deleting container %s", testFileName); + + rc = uuid_parse(testFileName, uuid); + DCHECK(rc, "Failed to parse 'testFile': %s", testFileName); + + rc = daos_cont_destroy(pool, uuid, 1 /* force */, NULL /* ev */); + if (rc != -DER_NONEXIST) + DCHECK(rc, "Failed to destroy container %s", testFileName); +} + +static char* DAOS_GetVersion() +{ + static char ver[1024] = {}; + + sprintf(ver, "%s", "DAOS"); + return ver; +} + +static void DAOS_Fsync(void *file, IOR_param_t *param) +{ + while (param->daosAios - nAios > 0) + AIOWait(param); +} + +static IOR_offset_t DAOS_GetFileSize(IOR_param_t *test, MPI_Comm testComm, + char *testFileName) +{ + /* + * Sizes are inapplicable to containers at the moment. + */ + return 0; +} diff --git a/src/aiori.c b/src/aiori.c index f5d5719..5978ed2 100644 --- a/src/aiori.c +++ b/src/aiori.c @@ -57,6 +57,9 @@ ior_aiori_t *available_aiori[] = { #endif #ifdef USE_RADOS_AIORI &rados_aiori, +#endif +#ifdef USE_DAOS_AIORI + &daos_aiori, #endif NULL }; @@ -133,7 +136,7 @@ char* aiori_get_version() static int is_initialized = FALSE; -void aiori_initialize(){ +void aiori_initialize(IOR_test_t *tests_head){ if (is_initialized) return; is_initialized = TRUE; @@ -145,18 +148,18 @@ void aiori_initialize(){ for (ior_aiori_t **tmp = available_aiori ; *tmp != NULL; ++tmp) { if((*tmp)->initialize){ - (*tmp)->initialize(); + (*tmp)->initialize(tests_head ? &tests_head->params : NULL); } } } -void aiori_finalize(){ +void aiori_finalize(IOR_test_t *tests_head){ if (! is_initialized) return; is_initialized = FALSE; for (ior_aiori_t **tmp = available_aiori ; *tmp != NULL; ++tmp) { if((*tmp)->finalize){ - (*tmp)->finalize(); + (*tmp)->finalize(tests_head ? &tests_head->params : NULL); } } } diff --git a/src/aiori.h b/src/aiori.h index 0b0ffda..c1f63e5 100755 --- a/src/aiori.h +++ b/src/aiori.h @@ -79,8 +79,8 @@ typedef struct ior_aiori { int (*rmdir) (const char *path, IOR_param_t * param); int (*access) (const char *path, int mode, IOR_param_t * param); int (*stat) (const char *path, struct stat *buf, IOR_param_t * param); - void (*initialize)(); /* called once per program before MPI is started */ - void (*finalize)(); /* called once per program after MPI is shutdown */ + void (*initialize)(IOR_param_t *); /* called once per program before MPI is started */ + void (*finalize)(IOR_param_t *); /* called once per program after MPI is shutdown */ option_help * (*get_options)(); } ior_aiori_t; @@ -96,9 +96,10 @@ extern ior_aiori_t s3_aiori; extern ior_aiori_t s3_plus_aiori; extern ior_aiori_t s3_emc_aiori; extern ior_aiori_t rados_aiori; +extern ior_aiori_t daos_aiori; -void aiori_initialize(); -void aiori_finalize(); +void aiori_initialize(IOR_test_t *th); +void aiori_finalize(IOR_test_t *th); const ior_aiori_t *aiori_select (const char *api); int aiori_count (void); void aiori_supported_apis(char * APIs); diff --git a/src/ior.c b/src/ior.c index 1fed65e..fbb6922 100755 --- a/src/ior.c +++ b/src/ior.c @@ -98,8 +98,6 @@ int ior_main(int argc, char **argv) out_logfile = stdout; out_resultfile = stdout; - aiori_initialize(); - /* * check -h option from commandline without starting MPI; */ @@ -125,6 +123,8 @@ int ior_main(int argc, char **argv) PrintHeader(argc, argv); + aiori_initialize(tests_head); + /* perform each test */ for (tptr = tests_head; tptr != NULL; tptr = tptr->next) { verbose = tptr->params.verbose; @@ -143,6 +143,8 @@ int ior_main(int argc, char **argv) ShowTestEnd(tptr); } + aiori_finalize(tests_head); + if (verbose < 0) /* always print final summary */ verbose = 0; @@ -155,8 +157,6 @@ int ior_main(int argc, char **argv) MPI_CHECK(MPI_Finalize(), "cannot finalize MPI"); - aiori_finalize(); - return totalErrorCount; } @@ -199,6 +199,11 @@ void init_IOR_Param_t(IOR_param_t * p) p->setAlignment = 1; p->lustre_start_ost = -1; + p->daosRecordSize = 262144; + p->daosStripeSize = 524288; + p->daosStripeCount = -1; + p->daosAios = 1; + hdfs_user = getenv("USER"); if (!hdfs_user) hdfs_user = ""; @@ -297,7 +302,8 @@ static void CheckFileSize(IOR_test_t *test, IOR_offset_t dataMoved, int rep) 1, MPI_LONG_LONG_INT, MPI_SUM, testComm), "cannot total data moved"); - if (strcasecmp(params->api, "HDF5") != 0 && strcasecmp(params->api, "NCMPI") != 0) { + if (strcasecmp(params->api, "HDF5") != 0 && strcasecmp(params->api, "NCMPI") != 0 && + strcasecmp(params->api, "DAOS") != 0) { if (verbose >= VERBOSE_0 && rank == 0) { if ((params->expectedAggFileSize != results[rep].aggFileSizeFromXfer) @@ -913,7 +919,8 @@ static void RemoveFile(char *testFileName, int filePerProc, IOR_param_t * test) rankOffset = 0; GetTestFileName(testFileName, test); } - if (backend->access(testFileName, F_OK, test) == 0) { + if (backend->access(testFileName, F_OK, test) == 0 || + strcasecmp(test->api, "DAOS") == 0) { backend->delete(testFileName, test); } if (test->reorderTasksRandom == TRUE) { @@ -921,7 +928,8 @@ static void RemoveFile(char *testFileName, int filePerProc, IOR_param_t * test) GetTestFileName(testFileName, test); } } else { - if ((rank == 0) && (backend->access(testFileName, F_OK, test) == 0)) { + if ((rank == 0) && (backend->access(testFileName, F_OK, test) == 0 || + strcasecmp(test->api, "DAOS"))) { backend->delete(testFileName, test); } } diff --git a/src/ior.h b/src/ior.h index 43177fd..4fa9052 100755 --- a/src/ior.h +++ b/src/ior.h @@ -190,6 +190,21 @@ typedef struct int lustre_set_striping; /* flag that we need to set lustre striping */ int lustre_ignore_locks; + /* DAOS variables */ + char daosGroup[MAX_STR]; /* group name */ + char daosPool[37]; /* pool UUID */ + char daosPoolSvc[MAX_STR]; /* pool service ranks */ + int daosRecordSize; /* size of akey record (i.e., rx_rsize) */ + int daosStripeSize; + unsigned long daosStripeCount; + unsigned long daosStripeMax; /* max length of a stripe */ + int daosAios; /* max number of concurrent async I/Os */ + int daosWriteOnly; /* write only, no flush and commit */ + unsigned long daosEpoch; /* epoch to access */ + unsigned long daosWait; /* epoch to wait for before reading */ + int daosKill; /* kill a target while running IOR */ + char daosObjectClass[MAX_STR]; /* object class */ + /* gpfs variables */ int gpfs_hint_access; /* use gpfs "access range" hint */ int gpfs_release_token; /* immediately release GPFS tokens after diff --git a/src/list.h b/src/list.h new file mode 100644 index 0000000..dbe052c --- /dev/null +++ b/src/list.h @@ -0,0 +1,556 @@ +/** + * GPL HEADER START + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 for more details (a copy is included + * in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * GPL HEADER END + */ +#ifndef __DAOS_LIST_H__ +#define __DAOS_LIST_H__ + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +#define prefetch(a) ((void)a) + +struct cfs_list_head { + struct cfs_list_head *next, *prev; +}; + +typedef struct cfs_list_head cfs_list_t; + +#define CFS_LIST_HEAD_INIT(name) { &(name), &(name) } + +#define CFS_LIST_HEAD(name) \ + cfs_list_t name = CFS_LIST_HEAD_INIT(name) + +#define CFS_INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/** + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __cfs_list_add(cfs_list_t * new, + cfs_list_t * prev, + cfs_list_t * next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * Insert an entry at the start of a list. + * \param new new entry to be inserted + * \param head list to add it to + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void cfs_list_add(cfs_list_t *new, + cfs_list_t *head) +{ + __cfs_list_add(new, head, head->next); +} + +/** + * Insert an entry at the end of a list. + * \param new new entry to be inserted + * \param head list to add it to + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void cfs_list_add_tail(cfs_list_t *new, + cfs_list_t *head) +{ + __cfs_list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __cfs_list_del(cfs_list_t *prev, + cfs_list_t *next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * Remove an entry from the list it is currently in. + * \param entry the entry to remove + * Note: list_empty(entry) does not return true after this, the entry is in an + * undefined state. + */ +static inline void cfs_list_del(cfs_list_t *entry) +{ + __cfs_list_del(entry->prev, entry->next); +} + +/** + * Remove an entry from the list it is currently in and reinitialize it. + * \param entry the entry to remove. + */ +static inline void cfs_list_del_init(cfs_list_t *entry) +{ + __cfs_list_del(entry->prev, entry->next); + CFS_INIT_LIST_HEAD(entry); +} + +/** + * Remove an entry from the list it is currently in and insert it at the start + * of another list. + * \param list the entry to move + * \param head the list to move it to + */ +static inline void cfs_list_move(cfs_list_t *list, + cfs_list_t *head) +{ + __cfs_list_del(list->prev, list->next); + cfs_list_add(list, head); +} + +/** + * Remove an entry from the list it is currently in and insert it at the end of + * another list. + * \param list the entry to move + * \param head the list to move it to + */ +static inline void cfs_list_move_tail(cfs_list_t *list, + cfs_list_t *head) +{ + __cfs_list_del(list->prev, list->next); + cfs_list_add_tail(list, head); +} + +/** + * Test whether a list is empty + * \param head the list to test. + */ +static inline int cfs_list_empty(cfs_list_t *head) +{ + return head->next == head; +} + +/** + * Test whether a list is empty and not being modified + * \param head the list to test + * + * Tests whether a list is empty _and_ checks that no other CPU might be + * in the process of modifying either member (next or prev) + * + * NOTE: using cfs_list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is cfs_list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + */ +static inline int cfs_list_empty_careful(const cfs_list_t *head) +{ + cfs_list_t *next = head->next; + return (next == head) && (next == head->prev); +} + +static inline void __cfs_list_splice(cfs_list_t *list, + cfs_list_t *head) +{ + cfs_list_t *first = list->next; + cfs_list_t *last = list->prev; + cfs_list_t *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * Join two lists + * \param list the new list to add. + * \param head the place to add it in the first list. + * + * The contents of \a list are added at the start of \a head. \a list is in an + * undefined state on return. + */ +static inline void cfs_list_splice(cfs_list_t *list, + cfs_list_t *head) +{ + if (!cfs_list_empty(list)) + __cfs_list_splice(list, head); +} + +/** + * Join two lists and reinitialise the emptied list. + * \param list the new list to add. + * \param head the place to add it in the first list. + * + * The contents of \a list are added at the start of \a head. \a list is empty + * on return. + */ +static inline void cfs_list_splice_init(cfs_list_t *list, + cfs_list_t *head) +{ + if (!cfs_list_empty(list)) { + __cfs_list_splice(list, head); + CFS_INIT_LIST_HEAD(list); + } +} + +/** + * Get the container of a list + * \param ptr the embedded list. + * \param type the type of the struct this is embedded in. + * \param member the member name of the list within the struct. + */ +#define cfs_list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(char *)(&((type *)0)->member))) + +/** + * Iterate over a list + * \param pos the iterator + * \param head the list to iterate over + * + * Behaviour is undefined if \a pos is removed from the list in the body of the + * loop. + */ +#define cfs_list_for_each(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, prefetch(pos->next)) + +/** + * Iterate over a list safely + * \param pos the iterator + * \param n temporary storage + * \param head the list to iterate over + * + * This is safe to use if \a pos could be removed from the list in the body of + * the loop. + */ +#define cfs_list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * Iterate over a list continuing after existing point + * \param pos the type * to use as a loop counter + * \param head the list head + * \param member the name of the list_struct within the struct + */ +#define cfs_list_for_each_entry_continue(pos, head, member) \ + for (pos = cfs_list_entry(pos->member.next, typeof(*pos), member); \ + prefetch(pos->member.next), &pos->member != (head); \ + pos = cfs_list_entry(pos->member.next, typeof(*pos), member)) + +/** + * \defgroup hlist Hash List + * Double linked lists with a single pointer list head. + * Mostly useful for hash tables where the two pointer list head is too + * wasteful. You lose the ability to access the tail in O(1). + * @{ + */ + +typedef struct cfs_hlist_node { + struct cfs_hlist_node *next, **pprev; +} cfs_hlist_node_t; + +typedef struct cfs_hlist_head { + cfs_hlist_node_t *first; +} cfs_hlist_head_t; + +/* @} */ + +/* + * "NULL" might not be defined at this point + */ +#ifdef NULL +#define NULL_P NULL +#else +#define NULL_P ((void *)0) +#endif + +/** + * \addtogroup hlist + * @{ + */ + +#define CFS_HLIST_HEAD_INIT { NULL_P } +#define CFS_HLIST_HEAD(name) cfs_hlist_head_t name = { NULL_P } +#define CFS_INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL_P) +#define CFS_INIT_HLIST_NODE(ptr) ((ptr)->next = NULL_P, (ptr)->pprev = NULL_P) + +static inline int cfs_hlist_unhashed(const cfs_hlist_node_t *h) +{ + return !h->pprev; +} + +static inline int cfs_hlist_empty(const cfs_hlist_head_t *h) +{ + return !h->first; +} + +static inline void __cfs_hlist_del(cfs_hlist_node_t *n) +{ + cfs_hlist_node_t *next = n->next; + cfs_hlist_node_t **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void cfs_hlist_del(cfs_hlist_node_t *n) +{ + __cfs_hlist_del(n); +} + +static inline void cfs_hlist_del_init(cfs_hlist_node_t *n) +{ + if (n->pprev) { + __cfs_hlist_del(n); + CFS_INIT_HLIST_NODE(n); + } +} + +static inline void cfs_hlist_add_head(cfs_hlist_node_t *n, + cfs_hlist_head_t *h) +{ + cfs_hlist_node_t *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + +/* next must be != NULL */ +static inline void cfs_hlist_add_before(cfs_hlist_node_t *n, + cfs_hlist_node_t *next) +{ + n->pprev = next->pprev; + n->next = next; + next->pprev = &n->next; + *(n->pprev) = n; +} + +static inline void cfs_hlist_add_after(cfs_hlist_node_t *n, + cfs_hlist_node_t *next) +{ + next->next = n->next; + n->next = next; + next->pprev = &n->next; + + if(next->next) + next->next->pprev = &next->next; +} + +#define cfs_hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define cfs_hlist_for_each(pos, head) \ + for (pos = (head)->first; pos && (prefetch(pos->next), 1); \ + pos = pos->next) + +#define cfs_hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos && (n = pos->next, 1); \ + pos = n) + +/** + * Iterate over an hlist of given type + * \param tpos the type * to use as a loop counter. + * \param pos the &struct hlist_node to use as a loop counter. + * \param head the head for your list. + * \param member the name of the hlist_node within the struct. + */ +#define cfs_hlist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = cfs_hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * Iterate over an hlist continuing after existing point + * \param tpos the type * to use as a loop counter. + * \param pos the &struct hlist_node to use as a loop counter. + * \param member the name of the hlist_node within the struct. + */ +#define cfs_hlist_for_each_entry_continue(tpos, pos, member) \ + for (pos = (pos)->next; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = cfs_hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * Iterate over an hlist continuing from an existing point + * \param tpos the type * to use as a loop counter. + * \param pos the &struct hlist_node to use as a loop counter. + * \param member the name of the hlist_node within the struct. + */ +#define cfs_hlist_for_each_entry_from(tpos, pos, member) \ + for (; pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = cfs_hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * Iterate over an hlist of given type safe against removal of list entry + * \param tpos the type * to use as a loop counter. + * \param pos the &struct hlist_node to use as a loop counter. + * \param n another &struct hlist_node to use as temporary storage + * \param head the head for your list. + * \param member the name of the hlist_node within the struct. + */ +#define cfs_hlist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = cfs_hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + +/* @} */ + +#ifndef cfs_list_for_each_prev +/** + * Iterate over a list in reverse order + * \param pos the &struct list_head to use as a loop counter. + * \param head the head for your list. + */ +#define cfs_list_for_each_prev(pos, head) \ + for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \ + pos = pos->prev, prefetch(pos->prev)) + +#endif /* cfs_list_for_each_prev */ + +#ifndef cfs_list_for_each_entry +/** + * Iterate over a list of given type + * \param pos the type * to use as a loop counter. + * \param head the head for your list. + * \param member the name of the list_struct within the struct. + */ +#define cfs_list_for_each_entry(pos, head, member) \ + for (pos = cfs_list_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = cfs_list_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) +#endif /* cfs_list_for_each_entry */ + +#ifndef cfs_list_for_each_entry_rcu +#define cfs_list_for_each_entry_rcu(pos, head, member) \ + list_for_each_entry(pos, head, member) +#endif + +#ifndef cfs_list_for_each_entry_rcu +#define cfs_list_for_each_entry_rcu(pos, head, member) \ + list_for_each_entry(pos, head, member) +#endif + +#ifndef cfs_list_for_each_entry_reverse +/** + * Iterate backwards over a list of given type. + * \param pos the type * to use as a loop counter. + * \param head the head for your list. + * \param member the name of the list_struct within the struct. + */ +#define cfs_list_for_each_entry_reverse(pos, head, member) \ + for (pos = cfs_list_entry((head)->prev, typeof(*pos), member); \ + prefetch(pos->member.prev), &pos->member != (head); \ + pos = cfs_list_entry(pos->member.prev, typeof(*pos), member)) +#endif /* cfs_list_for_each_entry_reverse */ + +#ifndef cfs_list_for_each_entry_safe +/** + * Iterate over a list of given type safe against removal of list entry + * \param pos the type * to use as a loop counter. + * \param n another type * to use as temporary storage + * \param head the head for your list. + * \param member the name of the list_struct within the struct. + */ +#define cfs_list_for_each_entry_safe(pos, n, head, member) \ + for (pos = cfs_list_entry((head)->next, typeof(*pos), member), \ + n = cfs_list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = cfs_list_entry(n->member.next, typeof(*n), member)) + +#endif /* cfs_list_for_each_entry_safe */ + +#ifndef cfs_list_for_each_entry_safe_from +/** + * Iterate over a list continuing from an existing point + * \param pos the type * to use as a loop cursor. + * \param n another type * to use as temporary storage + * \param head the head for your list. + * \param member the name of the list_struct within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define cfs_list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = cfs_list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = cfs_list_entry(n->member.next, typeof(*n), member)) +#endif /* cfs_list_for_each_entry_safe_from */ + +#define cfs_list_for_each_entry_typed(pos, head, type, member) \ + for (pos = cfs_list_entry((head)->next, type, member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = cfs_list_entry(pos->member.next, type, member), \ + prefetch(pos->member.next)) + +#define cfs_list_for_each_entry_reverse_typed(pos, head, type, member) \ + for (pos = cfs_list_entry((head)->prev, type, member); \ + prefetch(pos->member.prev), &pos->member != (head); \ + pos = cfs_list_entry(pos->member.prev, type, member)) + +#define cfs_list_for_each_entry_safe_typed(pos, n, head, type, member) \ + for (pos = cfs_list_entry((head)->next, type, member), \ + n = cfs_list_entry(pos->member.next, type, member); \ + &pos->member != (head); \ + pos = n, n = cfs_list_entry(n->member.next, type, member)) + +#define cfs_list_for_each_entry_safe_from_typed(pos, n, head, type, member) \ + for (n = cfs_list_entry(pos->member.next, type, member); \ + &pos->member != (head); \ + pos = n, n = cfs_list_entry(n->member.next, type, member)) + +#define cfs_hlist_for_each_entry_typed(tpos, pos, head, type, member) \ + for (pos = (head)->first; \ + pos && (prefetch(pos->next), 1) && \ + (tpos = cfs_hlist_entry(pos, type, member), 1); \ + pos = pos->next) + +#define cfs_hlist_for_each_entry_safe_typed(tpos, pos, n, head, type, member) \ + for (pos = (head)->first; \ + pos && (n = pos->next, 1) && \ + (tpos = cfs_hlist_entry(pos, type, member), 1); \ + pos = n) + +#endif /* __DAOS_LIST_H__ */ diff --git a/src/mdtest-main.c b/src/mdtest-main.c index 854456f..f54cf66 100644 --- a/src/mdtest-main.c +++ b/src/mdtest-main.c @@ -2,12 +2,12 @@ #include "aiori.h" int main(int argc, char **argv) { - aiori_initialize(); MPI_Init(&argc, &argv); + aiori_initialize(NULL); mdtest_run(argc, argv, MPI_COMM_WORLD, stdout); + aiori_finalize(NULL); MPI_Finalize(); - aiori_finalize(); return 0; } diff --git a/src/parse_options.c b/src/parse_options.c index 80b99a3..0f251ef 100755 --- a/src/parse_options.c +++ b/src/parse_options.c @@ -300,6 +300,34 @@ void DecodeDirective(char *line, IOR_param_t *params) params->numTasks = atoi(value); } else if (strcasecmp(option, "summaryalways") == 0) { params->summary_every_test = atoi(value); + } else if (strcasecmp(option, "daosgroup") == 0) { + strcpy(params->daosGroup, value); + } else if (strcasecmp(option, "daospool") == 0) { + strcpy(params->daosPool, value); + } else if (strcasecmp(option, "daospoolsvc") == 0) { + strcpy(params->daosPoolSvc, value); + } else if (strcasecmp(option, "daosrecordsize") == 0) { + params->daosRecordSize = string_to_bytes(value); + } else if (strcasecmp(option, "daosstripesize") == 0) { + printf("HERE %s\n", value); + params->daosStripeSize = string_to_bytes(value); + printf("HERE %d\n", params->daosStripeSize); + } else if (strcasecmp(option, "daosstripecount") == 0) { + params->daosStripeCount = atoi(value); + } else if (strcasecmp(option, "daosstripemax") == 0) { + params->daosStripeMax = string_to_bytes(value); + } else if (strcasecmp(option, "daosaios") == 0) { + params->daosAios = atoi(value); + } else if (strcasecmp(option, "daosepoch") == 0) { + params->daosEpoch = atoi(value); + } else if (strcasecmp(option, "daoswait") == 0) { + params->daosWait = atoi(value); + } else if (strcasecmp(option, "daoswriteonly") == 0) { + params->daosWriteOnly = atoi(value); + } else if (strcasecmp(option, "daoskill") == 0) { + params->daosKill = atoi(value); + } else if (strcasecmp(option, "daosobjectclass") == 0) { + strcpy(params->daosObjectClass, value); } else { if (rank == 0) fprintf(out_logfile, "Unrecognized parameter \"%s\"\n", @@ -311,11 +339,13 @@ void DecodeDirective(char *line, IOR_param_t *params) /* * Parse a single line, which may contain multiple comma-seperated directives */ -void ParseLine(char *line, IOR_param_t * test) +void ParseLine(const char *line, IOR_param_t * test) { char *start, *end; - start = line; + start = strdup(line); + if (start == NULL) + ERR("failed to duplicate line"); do { end = strchr(start, ','); if (end != NULL) @@ -422,7 +452,7 @@ IOR_test_t *ReadConfigScript(char *scriptName) static IOR_param_t * parameters; static void decodeDirectiveWrapper(char *line){ - DecodeDirective(line, parameters); + ParseLine(line, parameters); } /*