diff --git a/.gitignore b/.gitignore index ff11029e86a8bd343ea6ebeb5197a0ca466b366a..27129c2ec3ef95991386f20cf38d559130a237bc 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ install/ benchmark/cuda_kernels.cc # Ignore executables **/linktest +!**/linktest/ **/linktest.mpi **/linktest.tcp **/linktest.cuda diff --git a/ImHex_Patterns/LinkTest_2-2-0_SIONlib_1-7-7.hexpat b/ImHex_Patterns/LinkTest_2-2-0_SIONlib_1-7-7.hexpat new file mode 100644 index 0000000000000000000000000000000000000000..55c164a283793e0dd3b42dabd2cb94ebedcd1c3e --- /dev/null +++ b/ImHex_Patterns/LinkTest_2-2-0_SIONlib_1-7-7.hexpat @@ -0,0 +1,221 @@ +#include <std/sys> +#include <std/io> + +// Valid for LinkTest Version 2.2.0 +// Valid for SIONlib 1.7.7 + +// ImHex Bugs: +// 1: Attributes applied to an array do not inherit to its elements. +// This makes conversion of the displayed values in case of endinaness changes impossible. + +// TODO: Currently only works if there is only one SION block in total and one SION chunk per task. + +u64 offset; +u64 numIter; +bool bes; //If true SION headers are big endian +bool bel; //If true LinkTest data is big-endian + +struct SION_Header_{ + if(bes){ + be u32 Version; + be u32 Patchlevel; + be u32 File_version; + be u32 Block_Size; + be u32 Num_Tasks; + be u32 Num_Files; + be u32 File_Num; + }else{ + le u32 Version; + le u32 Patchlevel; + le u32 File_version; + le u32 Block_Size; + le u32 Num_Tasks; + le u32 Num_Files; + le u32 File_Num; + } + padding[16]; + char Filename[1024]; + if(bes){ + be u64 GlobalRank[Num_Tasks]; + be u64 Chunk_Size[Num_Tasks]; + be u32 Max_Chunks; + be u64 Var_Start; + }else{ + le u64 GlobalRank[Num_Tasks]; + le u64 Chunk_Size[Num_Tasks]; + le u32 Max_Chunks; + le u64 Var_Start; + } + if(Block_Size>1){ + padding[Block_Size-$]; + } +}; + +struct LinkTest_Header_{ + if(bel){ + be u32 Major_Version; + be u32 Minor_Version; + be u32 Patchlevel; + }else{ + le u32 Major_Version; + le u32 Minor_Version; + le u32 Patchlevel; + } + char Git_Hash[41]; + if(bel){ + be u32 Mode_Length [[hidden]]; + }else{ + le u32 Mode_Length [[hidden]]; + } + char Mode[Mode_Length]; + u8 A2A_Flag; + u8 Bidirectional_Flag; + u8 Unidirectional_Flag; + u8 Bisection_Flag; + u8 Step_Randomization_Flag; + u8 Serialize_Testing_Flag; + u8 No_SION_File_Flag; + u8 Parallel_SION_File_Flag; + u8 GPU_Memory_Flag; + u8 Multi_Buffer_Flag; + u8 Randomized_Buffer_Flag; + u8 Check_Buffer_Flag; + u8 Group_Hostname_Flag; + u8 Memory_Allocator_Type; + u16 Padding; + u8 Padding2; + if(bel){ + be u64 Num_Messages; + be u64 Size_Messages; + be u64 Num_WarmUp_Messages; + be u64 Num_Serial_Retests; + be u64 Num_Multi_Buffer; + be u64 Buffer_Randomization_Seed; + be u64 Num_Randomized_Task; + be u64 Task_Randomization_Seed; + be u64 Step_Randomization_Seed; + }else{ + le u64 Num_Messages; + le u64 Size_Messages; + le u64 Num_WarmUp_Messages; + le u64 Num_Serial_Retests; + le u64 Num_Multi_Buffer; + le u64 Buffer_Randomization_Seed; + le u64 Num_Randomized_Task; + le u64 Task_Randomization_Seed; + le u64 Step_Randomization_Seed; + } + char END_HEADER[10] [[hidden]]; + std::assert(END_HEADER=="END_HEADER","LinkTest header did not end with END_HEADER"); +}; + +struct Host_{ + if(bel){ + be u32 Hostname_Length [[hidden,transform("Change_Endianness_u32")]]; + }else{ + le u32 Hostname_Length [[hidden]]; + } + char Hostname[Hostname_Length]; + if(bel){ + be u32 Core_ID [[format("Format_Change_Endianness_u32"),transform("Change_Endianness_u32")]]; + }else{ + le u32 Core_ID; + } +}; + +struct IterationData_{ + if(rank==0){ + char Start_Time[32]; + if(bel){ + be double Minimum_Time [[format("Format_Change_Endianness_double"),transform("Change_Endianness_double")]]; + be double Average_Time [[format("Format_Change_Endianness_double"),transform("Change_Endianness_double")]]; + be double Maximum_Time [[format("Format_Change_Endianness_double"),transform("Change_Endianness_double")]]; + if(LinkTest_Header.A2A_Flag){ + be double A2A_Minimum_Time [[format("Format_Change_Endianness_double"),transform("Change_Endianness_double")]]; + be double A2A_Average_Time [[format("Format_Change_Endianness_double"),transform("Change_Endianness_double")]]; + be double A2A_Maximum_Time [[format("Format_Change_Endianness_double"),transform("Change_Endianness_double")]]; + } + be double New_Retested_Timings[LinkTest_Header.Num_Serial_Retests] [[format("Format_Change_Endianness_double"),transform("Change_Endianness_double")]]; + be double Old_Retested_Timings[LinkTest_Header.Num_Serial_Retests] [[format("Format_Change_Endianness_double"),transform("Change_Endianness_double")]]; + be u64 Retests_From[LinkTest_Header.Num_Serial_Retests] [[format("Format_Change_Endianness_u64"),transform("Change_Endianness_u64")]]; + be u64 Retests_To[LinkTest_Header.Num_Serial_Retests] [[format("Format_Change_Endianness_u64"),transform("Change_Endianness_u64")]]; + }else{ + le double Minimum_Time; + le double Average_Time; + le double Maximum_Time; + if(LinkTest_Header.A2A_Flag){ + le double A2A_Minimum_Time; + le double A2A_Average_Time; + le double A2A_Maximum_Time; + } + le double New_Retested_Timings[LinkTest_Header.Num_Serial_Retests]; + le double Old_Retested_Timings[LinkTest_Header.Num_Serial_Retests]; + le u64 Retests_From[LinkTest_Header.Num_Serial_Retests]; + le u64 Retests_To[LinkTest_Header.Num_Serial_Retests]; + } + char End_Time[32]; + } + if(bel){ + be double Timings[SION_Header.Num_Tasks]; + be u64 AccessPattern[SION_Header.Num_Tasks]; + if(LinkTest_Header.A2A_Flag){ + be double A2A_Time; + } + }else{ + le double Timings[SION_Header.Num_Tasks]; + le u64 AccessPattern[SION_Header.Num_Tasks]; + if(LinkTest_Header.A2A_Flag){ + le double A2A_Time; + } + } +}; + +struct Data_{ + Host_ Host; + IterationData_ Data[numIter]; + char END_BLOCK[9] [[hidden]]; + std::assert(END_BLOCK=="END_BLOCK","LinkTest per-rank data did not end with END_BLOCK"); + rank=rank+1; +}; + +char SION_String[4] @ 0x0 [[hidden]]; +std::assert(SION_String=="sion","File does not start with sion!"); +u8 Endianness_Flags[4] @ $ [[hidden]]; +offset=$; +std::assert(Endianness_Flags[0]==Endianness_Flags[3],"Endianness Flag not for SIONlib data not symmetric"); +if(Endianness_Flags[0]==1){ + bes=true; +}else{ + bes=false; +} +std::assert(Endianness_Flags[1]==Endianness_Flags[2],"Endianness Flag not for LinkTest data not symmetric"); +if(Endianness_Flags[1]==1){ + bel=true; +}else{ + bel=false; +} +$=offset; +SION_Header_ SION_Header @ $ [[comment("SIONlib File Header"), name("SIONlib Header")]]; +offset=$; +std::assert(SION_Header.Version==1007,"SION file not from SIONlib version 1.7.6 or 1.7.7!"); +std::assert(SION_Header.Patchlevel==7,"SION file not from SIONlib version 1.7.6 or 1.7.7!"); +std::assert(SION_Header.Num_Files==1,"No support for multi-file SIONlib data!"); +std::assert(SION_Header.Num_Files==1,"No support for multiple SION chunks!"); +$=offset; +char ID_String[8] @ $ [[hidden]]; +offset=$; +std::assert_warn(ID_String=="LinkTest","Failed to match LinkTest!"); +$=offset; +LinkTest_Header_ LinkTest_Header @ $ [[comment("LinkTest File Header"), name("LinkTest Header")]]; +offset=$; +std::assert_warn(LinkTest_Header.No_SION_File_Flag==0,"No-SION-File Flag set!"); +if(LinkTest_Header.Num_Randomized_Task!=0){ + numIter=LinkTest_Header.Num_Randomized_Task; +}else{ + numIter=1; +} +u64 rank; +$=offset; +Data_ Data[SION_Header.Num_Tasks] @ $; + + diff --git a/benchmark/benchmark.cc b/benchmark/benchmark.cc index ddb78ec98fca4a2ba0a8b190636554f52b7ea027..0a45937fdeb76eecee60b003cda5301f93eeacc5 100644 --- a/benchmark/benchmark.cc +++ b/benchmark/benchmark.cc @@ -659,7 +659,9 @@ int Benchmark::retest_slow_pairs(slow_pair* const sp,const int n,const int iter) const int to = sp[i].to; if (0 == rank()) { - std::printf(" %6d: Retest %6d <-> %6d:", i, from, to); + const char* from_host = cl->hostnamesAndRanks.hostForRank[from].c_str(); + const char* to_host = cl->hostnamesAndRanks.hostForRank[to].c_str(); + std::printf(" %6d: Retest %6d (%s) <-> %6d (%s):", i, from, from_host, to, to_host); std::fflush(stdout); } @@ -839,9 +841,8 @@ int Benchmark::init() { randomNumberEngineSteps = std::mt19937(0); } - if(args->do_group_processes_by_hostname > 0) { - cl->getHostAndLocalRank(); - } + // Run it always to gather hostname information + cl->getHostAndLocalRank(); return SUCCESS; } diff --git a/benchmark/cconfig.h b/benchmark/cconfig.h index f8e6ca60a33b6e47b0c36245e96678d57713cc8e..3b2e4c091bba842d755abc9242b1961eb2711672 100644 --- a/benchmark/cconfig.h +++ b/benchmark/cconfig.h @@ -28,8 +28,8 @@ #endif #define LINKTEST_VERSION 2 -#define LINKTEST_VERSION_SUB 1 -#define LINKTEST_VERSION_PATCHLEVEL 19 +#define LINKTEST_VERSION_SUB 2 +#define LINKTEST_VERSION_PATCHLEVEL 0 #define GIT_HASH_LEN 41 #ifndef GIT_HASH #define GIT_HASH "UNKNOWN" diff --git a/benchmark/vcluster.cc b/benchmark/vcluster.cc index 15f8dee60965f16eaabf6390725c6a71c25508da..722d39a7caa2b62259f250aa68797c83d2c4bc58 100644 --- a/benchmark/vcluster.cc +++ b/benchmark/vcluster.cc @@ -514,7 +514,7 @@ const std::string& VirtualCluster::get_vcluster_impl_name(char** argv, const std const std::string dot{"."}; auto pos = executableName.find(dot); if(pos != std::string::npos) { - requestedImpl = executableName.substr(pos); + requestedImpl = executableName.substr(pos+1); } // Check environment @@ -598,7 +598,10 @@ void VirtualCluster::getHostAndLocalRank(){ for(int i=1;i<size();i++){ int j=uhostnames.size()-1; for(;j>=0;j--){ //Iterate backwards as hostnames are likely to repeat - if(hostnames[i]!=uhostnames[j]) continue; + // Compare based on c strings to avoid mistmatches due to control characters + if(strcmp(hostnames[i].c_str(), uhostnames[j].c_str()) != 0){ + continue; + } else{ ranks[j].push_back(i); break; @@ -614,14 +617,25 @@ void VirtualCluster::getHostAndLocalRank(){ std::stringstream buf; for(std::vector<std::string>::size_type i=0;i<uhostnames.size();i++){ buf << uhostnames[i].c_str() << ": " << ranks[i]; - debug("[%3d] Ranks on %s", 0, buf.str().c_str());buf.str(std::string()); + debug("[%3d] Ranks on %s", 0, buf.str().c_str()); + buf.str(std::string()); } #endif /******************************************************/ /* Check that all hosts have the same number of ranks */ /******************************************************/ - if(ranks.size()%2!=0) fatal("An even number of hosts is required!"); - for(std::vector<std::vector<int>>::size_type i=0;i<ranks.size();i++) if(ranks[0].size()!=ranks[i].size()) fatal("Hosts have differing amounts of ranks!"); + // One host is allowed for intranode testing + if(ranks.size()%2 != 0 and ranks.size() != 1){ + fatal("An even number of hosts is required!"); + } + else if(size()%2 != 0){ + fatal("An even number of ranks is required!"); + } + for(std::vector<std::vector<int>>::size_type i=0;i<ranks.size();i++){ + if(ranks[0].size()!=ranks[i].size()){ + fatal("Hosts have differing amounts of ranks!"); + } + } /**************************************/ /* Broadcast information to all ranks */ @@ -661,6 +675,17 @@ void VirtualCluster::getHostAndLocalRank(){ } } bcast(0, hostLocalRanks_.get(), buf64x4[0]*buf64x4[2]); + + /* + * Fill in the hostnamesAndRanks struct to be queried later on + */ + for(int rank = 0; rank < size(); rank++){ + hostnamesAndRanks.hostForRank.push_back(hostnames[rank]); + } + for(std::vector<std::string>::size_type host=0;host<uhostnames.size();host++){ + hostnamesAndRanks.ranksForHost.push_back(ranks[host]); + } + }else{ gather(0, &tmp32, &tmp32, 1); //Gather hostname lengths send(0, hostname().c_str(), hostnameSize()); //Send hostname diff --git a/benchmark/vcluster.h b/benchmark/vcluster.h index b516073ed5a576c26509ecadd8bbf8856843d8d4..d2c65e5f1f29e5a2d02a40b54699fe4e5d33b30f 100644 --- a/benchmark/vcluster.h +++ b/benchmark/vcluster.h @@ -73,6 +73,11 @@ public: int localRank() ; std::shared_ptr<int[]> hostLocalRanks() ; + struct { + std::vector<std::string> hostForRank; + std::vector<std::vector<int>> ranksForHost; + } hostnamesAndRanks; + /*! \brief send wrapped data in buf to rank dst (communication layer undefined) * * The data may or may not be routed through the communication layer of this cluster diff --git a/exampleBuild.sh b/exampleBuild.sh index 07eac176bef43da8c92c99383a71a8edbd992ba5..57fb799101170f26572c336cf67fc830fba516fb 100755 --- a/exampleBuild.sh +++ b/exampleBuild.sh @@ -13,7 +13,7 @@ # Minipmi is already installed in ~/.local # Set-Up Environment -ml GCC ParaStationMPI SIONlib SciPy-Stack; +ml GCC OpenMPI SIONlib SciPy-Stack; # Use locallly installed minipmi, needed since we build with UCX and IBVerbs support export LIBRARY_PATH=$LIBRARY_PATH:~/.local/lib/; @@ -28,7 +28,7 @@ make clean cd ..; # Install linktest-report # FIX for JSC Systems -#export CPATH=/p/software/<SYSTEM>/stages/2022/software/SciPy-bundle/2021.10-gcccoremkl-11.2.0-2021.4.0/lib/python3.9/site-packages/numpy/core/include:$CPATH +#export CPATH=$EBROOTSCIPYMINBUNDLE/lib/python3*/site-packages/numpy/core/include:$CPATH cd install; python3 -m venv linktest-report-venv; source linktest-report-venv/bin/activate diff --git a/python/linktest/linktest.c b/python/linktest/linktest.c index c5640e03533223baa1f489609e456a3c86328d7e..2a22641df02f31100bf28f6ac12bb6bc430ff5b4 100644 --- a/python/linktest/linktest.c +++ b/python/linktest/linktest.c @@ -30,6 +30,7 @@ #include <linktest_2_1_17.h> #include <linktest_2_1_18.h> #include <linktest_2_1_19.h> +#include <linktest_2_2_0.h> /* TODO: Properly handle all possible errors. * Current plan is only to handle errors @@ -177,6 +178,13 @@ static PyObject* readSION(PyObject *self, PyObject *args){ err=1; } break; + case 2: + switch(version[2]){ + case 0: + err=readImport_2_2_0(&fh,swap,version,&pyLinkTestInfo,&pyLinkTestData); + break; + } + break; default: PyErr_SetString(PyExc_RuntimeError, "Unsupported Version!"); err=1; diff --git a/python/linktest/linktest_2_2_0.c b/python/linktest/linktest_2_2_0.c new file mode 100644 index 0000000000000000000000000000000000000000..e5bed1d2688e24cf1d9f1ff7c45ff1285fc0361d --- /dev/null +++ b/python/linktest/linktest_2_2_0.c @@ -0,0 +1,995 @@ +/**************************************************************************** +** LinkTest ** +***************************************************************************** +** Copyright (c) 2008-2025 ** +** Forschungszentrum Juelich, Juelich Supercomputing Centre ** +** ** +** See the file COPYRIGHT in the package base directory for details ** +****************************************************************************/ + +#include <linktest_DoNotIncludeInMain.h> +#include <linktest.h> +#include <linktest_helper.h> + +/*********************************/ +/* Constant File-Scope Variables */ +/*********************************/ +#define LINKTEST_OUTPUT_DATETIME_LENGTH_2_2_0 32 +#define GIT_HASH_SIZE_2_2_0 41 +#define LINKTEST_ID_STR_2_2_0 "LinkTest" +#define END_HEADER_2_2_0 "END_HEADER" +#define END_BLOCK_2_2_0 "END_BLOCK" + +/**************/ +/* Structures */ +/**************/ +struct LinkTestInfo_2_2_0{ //Description for LinkTest 2.2.0 + char hash[GIT_HASH_SIZE_2_2_0]; // LinkTest Git Hash + char *mode; // Virtual-Cluster Implementation + int8_t options[14]; /* LinkTest Options + * Unused in Version [1.X.X & 2.0.0] + * options[ 0]: If non-zero all-to-all + * tests were performed at + * the beginning and end of + * each iteration. + * options[ 1]: If non-zero bidirectional + * tests were performed. + * options[ 2]: If non-zero unidirectional + * tests were performed. + * options[ 3]: If non-zero bisection + * tests were performed. + * options[ 4]: If non-zero steps were + * mixed before tests were + * performed. + * options[ 5]: If non-zero main testing + * was performed in serial. + * options[ 6]: If non-zero output was + * written to a SION file. + * Always non-zero since + * these options were + * written to a SION file. + * options[ 7]: If non-zero the SION file + * was generated using + * parallelized output. + * options[ 8]: If non-zero messages were + * sent from device memory, + * likely GPU memory. + * options[ 9]: If non-zero multiple + * buffers were used. + * options[10]: If non-zero the buffers + * were filled with pseudo- + * random numbers. + * options[11]: If non-zero buffers were + * checked after each kernel + * run. + * options[12]: If non-zero processes were + * grouped according to hostname + * and testing only occurred + * between groups. + * options[13]: Memory allocator type: + * 0: Invalid - ERROR + * 1: Default - ERROR + * 2: Memory-aligned Malloc + * 3: Pinned-Memory Map + * 4: POSIX Memory-aligned Malloc + * 5: CUDA Malloc + */ + uint8_t Padding[ 3]; /* Padding that is added for at the moment unknown reasons */ + uint64_t iInfo[ 9]; /* Auxiliary Integer Information + * iInfo[ 0]: The number of messages + * timings were averaged + * over. + * iInfo[ 1]: The message size sent + * between tasks. + * iInfo[ 2]: The number of warm-up + * messages sent before + * beginning timing. + * iInfo[ 3]: Number of serial retests + * iInfo[ 4]: Number of memory buffers + * if multiple memory + * buffers were used. + * iInfo[ 5]: MT_19937 seed for buffer + * randomization.. + * iInfo[ 6]: If non-zero indicates the + * number of different task + * permutations used for + * averaging communication + * times. + * iInfo[ 7]: MT_19937 seed for task + * randomization. + * iInfo[ 8]: MT_19937 seed for step + * randomization. + */ +}; + +enum OPTIONS{ + A2A________FLAG= 0, + BIDIR______FLAG= 1, + UNIDIR_____FLAG= 2, + BISECT_____FLAG= 3, + RAND_STEP__FLAG= 4, + SERIAL_____FLAG= 5, + SION_OUT___FLAG= 6, + SION_PAR___FLAG= 7, + DEVICE_MEM_FLAG= 8, + MULTI_BUF__FLAG= 9, + RAND_BUF___FLAG=10, + CHECK_BUF__FLAG=11, + GROUP_HOST_FLAG=12, + MEM_ALLOC__TYPE=13 +}; + +enum INFO{ + NUM_MESSAGES=0, + MESSAGE_SIZE=1, + NUM_WARM__UP=2, + NUM___SERIAL=3, + NUM__MEM_BUF=4, + MEM_BUF_SEED=5, + NUM_PERMUTAT=6, + PERMUTA_SEED=7, + STEP____SEED=8 +}; + +/*****************************/ +/* Read LinkTest Information */ +/*****************************/ +int readInfo_2_2_0(struct SionFile* const fh, const int swap, const Version version, struct LinkTestInfo_2_2_0* const linkTestInfo){ + const size_t end_header_len=sizeof(END_HEADER_2_2_0)/sizeof(char); + char end_header[end_header_len]; + size_t n; + int32_t strSize; + + /* Git Hash */ + n=fread(linkTestInfo->hash, sizeof(char), GIT_HASH_SIZE_2_2_0, fh->fp); + if (UNLIKELY(n != GIT_HASH_SIZE_2_2_0)) { + PyErr_SetString(PyExc_RuntimeError, "readInfo_2_2_0: Failed to read git hash."); + return -1; + } + sion_swap(linkTestInfo->hash, linkTestInfo->hash, sizeof(char), GIT_HASH_SIZE_2_2_0, swap); + if (UNLIKELY(linkTestInfo->hash[0] == '\0')){ + PyErr_SetString(PyExc_RuntimeError, "readInfo_2_2_0: NULL git hashes are not supported."); + return -1; + } + linkTestInfo->hash[GIT_HASH_SIZE_2_2_0-1]='\0'; //Make sure string is null terminated + #ifdef DEBUG_OUTPUT + printf("git hash =%s\n",linkTestInfo->hash);fflush(stdout); + #endif + + /* Read Virtual Cluster Implementation */ + n=fread(&strSize, sizeof(int32_t), 1, fh->fp); + if (UNLIKELY(n != 1)) { + PyErr_SetString(PyExc_RuntimeError, "readInfo_2_2_0: Failed to read string sizes for git hash, datetime and mode."); + return -1; + } + sion_swap(&strSize, &strSize, sizeof(int32_t), 1, swap); + linkTestInfo->mode=malloc(strSize*sizeof(char)); //TODO: Dangerous if sizeof(char) mismatches between writing and reading system. + n=fread(linkTestInfo->mode, sizeof(char), strSize, fh->fp); + if (UNLIKELY((int32_t)n != strSize)) { + PyErr_SetString(PyExc_RuntimeError, "readInfo_2_2_0: Failed to read virtual-cluster implementation."); + return -1; + } + sion_swap(linkTestInfo->mode, linkTestInfo->mode, sizeof(char), strSize, swap); + if (UNLIKELY(linkTestInfo->mode[0] == '\0')){ + PyErr_SetString(PyExc_RuntimeError, "readInfo_2_2_0: Virtual-cluster implementation NULL is not supported."); + return -1; + } + linkTestInfo->mode[strSize-1]='\0'; //Add null terminator to string + #ifdef DEBUG_OUTPUT + printf("mode =%s\n",linkTestInfo->mode);fflush(stdout); + #endif + + /* Read 32-bit Integer Options */ + n=fread(linkTestInfo->options, sizeof(int8_t), 14, fh->fp); + if (UNLIKELY(n != 14)) { + PyErr_SetString(PyExc_RuntimeError, "readInfo_2_2_0: Failed to read 32-bit integer LinkTest options."); + return -1; + } + sion_swap(linkTestInfo->options, linkTestInfo->options, sizeof(int8_t), 14, swap); + #ifdef DEBUG_OUTPUT + printf("do_alltoall =%" PRId8 "\n",linkTestInfo->options[A2A________FLAG]); + printf("do_bidir =%" PRId8 "\n",linkTestInfo->options[BIDIR______FLAG]); + printf("do_unidir =%" PRId8 "\n",linkTestInfo->options[UNIDIR_____FLAG]); + printf("do_bisection =%" PRId8 "\n",linkTestInfo->options[BISECT_____FLAG]); + printf("do_mix =%" PRId8 "\n",linkTestInfo->options[RAND_STEP__FLAG]); + printf("do_serial =%" PRId8 "\n",linkTestInfo->options[SERIAL_____FLAG]); + printf("do_nosion =%" PRId8 "\n",linkTestInfo->options[SION_OUT___FLAG]); + printf("do_sion_par =%" PRId8 "\n",linkTestInfo->options[SION_PAR___FLAG]); + printf("do_use_gpus =%" PRId8 "\n",linkTestInfo->options[DEVICE_MEM_FLAG]); + printf("use_multi_buf =%" PRId8 "\n",linkTestInfo->options[MULTI_BUF__FLAG]); + printf("randomize_buffers =%" PRId8 "\n",linkTestInfo->options[RAND_BUF___FLAG]); + printf("check_buffers =%" PRId8 "\n",linkTestInfo->options[CHECK_BUF__FLAG]); + printf("group_processes_by_hostname=%" PRId8 "\n",linkTestInfo->options[GROUP_HOST_FLAG]); + printf("memory allocator type =%" PRId8 "\n",linkTestInfo->options[MEM_ALLOC__TYPE]); + fflush(stdout); + #endif + + /* Read padding */ + n=fread(linkTestInfo->options, sizeof(int8_t), 3, fh->fp); + if (UNLIKELY(n != 3)) { + PyErr_SetString(PyExc_RuntimeError, "readInfo_2_2_0: Failed to read padding."); + return -1; + } + /* Read 32-bit Integer Information */ + n=fread(linkTestInfo->iInfo, sizeof(uint64_t), 9, fh->fp); + if (UNLIKELY(n != 9)) { + PyErr_SetString(PyExc_RuntimeError, "readInfo_2_2_0: Failed to read 32-bit integer LinkTest information."); + return -1; + } + sion_swap(linkTestInfo->iInfo, linkTestInfo->iInfo, sizeof(uint64_t), 9, swap); + #ifdef DEBUG_OUTPUT + printf("num_msg =%" PRIu64 "\n",linkTestInfo->iInfo[NUM_MESSAGES]); + printf("len_msg =%" PRIu64 "\n",linkTestInfo->iInfo[MESSAGE_SIZE]); + printf("num_warmup_msg =%" PRIu64 "\n",linkTestInfo->iInfo[NUM_WARM__UP]); + printf("max_stest =%" PRIu64 "\n",linkTestInfo->iInfo[NUM___SERIAL]); + printf("num_multi_buf =%" PRIu64 "\n",linkTestInfo->iInfo[NUM__MEM_BUF]); + printf("buf_mt_seed =%" PRIu64 "\n",linkTestInfo->iInfo[MEM_BUF_SEED]); + printf("num_randomize_tasks =%" PRIu64 "\n",linkTestInfo->iInfo[NUM_PERMUTAT]); + printf("seed_randomize_tasks =%" PRIu64 "\n",linkTestInfo->iInfo[PERMUTA_SEED]); + printf("seed_randomize_steps =%" PRIu64 "\n",linkTestInfo->iInfo[STEP____SEED]); + fflush(stdout); + #endif + + /* Check If 0/1 Multiple Buffer Were Specified */ + if(linkTestInfo->options[MULTI_BUF__FLAG] != 0){ + if(linkTestInfo->iInfo[NUM__MEM_BUF] == 0){ + PyErr_WarnEx(NULL,"Detected that multiple buffers were used, however, an unknown (0) number of buffers were used.\n ReadSION: The number of buffers used was likely equal to the number of messages. Interpret results with caution.",(Py_ssize_t )1); + }else if(linkTestInfo->iInfo[NUM__MEM_BUF] == 1){ + PyErr_WarnEx(NULL,"Detected that multiple buffers were used, however, only one buffer was used.\n ReadSION: In this scenario multiple buffers should not have been used. Interpret results with caution.",(Py_ssize_t )1); + } + } + + /* Check If Mersenne Twister Seed Is Zero */ + if(linkTestInfo->options[RAND_BUF___FLAG] != 0 && linkTestInfo->iInfo[MEM_BUF_SEED] == 0){ + PyErr_WarnEx(NULL,"Buffer content was to be randomized, however, the stored zero Mersenne Twister seed would have filled the buffers with zero. Interpret results with caution.",(Py_ssize_t )1); + } + + /* Check END_HEADER String */ + n=fread(end_header,sizeof(char),end_header_len-1,fh->fp); + if (UNLIKELY(n != end_header_len-1)){ + PyErr_SetString(PyExc_RuntimeError, "readImportData_2_2_0: Failed to read END_HEADER string."); + return -1; + } + end_header[end_header_len-1]='\0'; + if(UNLIKELY(strcmp(end_header,END_HEADER_2_2_0))){ + PyErr_SetString(PyExc_RuntimeError, "readImportData_2_2_0: Alignment Error Detected. \"END_HEADER\" not found at the correct location."); + return -1; + } + + return 0; +} + +/*******************************************/ +/* Import LinkTest Information Into Python */ +/*******************************************/ +int importInfo_2_2_0(const Version version, struct LinkTestInfo_2_2_0* const linkTestInfo, PyObject* const* const pyLinkTestInfo){ + int err; + + // Allocator Type + enum AllocatorType{ + AllocatorInvalid =0, //Invalid Allocator - ERROR + AllocatorDefault =1, //Default Allocator - ERROR + AllocatorAlignedMalloc =2, //Memory-aligned malloc Allocator + AllocatorPinnedMmap =3, //Pinned--memory-map Allocator + AllocatorPOSIXAlignedMalloc=4, //POSIX Memory-aligned malloc Allocator + AllocatorCUDA =5 //CUDA malloc Allocator + }; + + /* Set LinkTest Version */ + /* TODO: There is a potential bug here. %d is used for a digit type, + * however this may be insufficiently long for 32-bit version numbers. + * This can cause errors. Python, however, does not support the standard + * C way of using PRId32. + */ + err=PyDict_SetItem(*pyLinkTestInfo, + PyUnicode_FromString("Version"), + PyUnicode_FromFormat("%d.%d.%d",version[0],version[1],version[2]) + ); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add Version to LinkTestInfo dictionary."); + return -1; + } + + /* Set LinkTest Git Hash */ + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Git hash" ),PyUnicode_FromString(linkTestInfo->hash )); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add hash to LinkTestInfo dictionary."); + return -1; + } + + /* Set Virtual-Cluster Implementation */ + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Mode" ),PyUnicode_FromString(linkTestInfo->mode)); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add mode to LinkTestInfo dictionary."); + return -1; + } + free(linkTestInfo->mode); + + /* Set Options */ + // NOTE: Casting to a long int from an int32_t to a long int should + // be safe since a long int on standard compliant compilers have a + // minimum size of 32 bytes. + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Test MPI all-to-all?" ),PyLong_FromLong((long int)linkTestInfo->options[A2A________FLAG])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add doAlltoall to LinkTestInfo dictionary."); + return -1; + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Do bidirectional tests?" ),PyLong_FromLong((long int)linkTestInfo->options[BIDIR______FLAG])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add doBidir to LinkTestInfo dictionary."); + return -1; + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Do unidirectional tests?" ),PyLong_FromLong((long int)linkTestInfo->options[UNIDIR_____FLAG])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add doUnidir to LinkTestInfo dictionary."); + return -1; + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Do a bisection test?" ),PyLong_FromLong((long int)linkTestInfo->options[BISECT_____FLAG])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add doBisec to LinkTestInfo dictionary."); + return -1; + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Randomize step order?" ),PyLong_FromLong((long int)linkTestInfo->options[RAND_STEP__FLAG])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add doMix to LinkTestInfo dictionary."); + return -1; + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Test serially?" ),PyLong_FromLong((long int)linkTestInfo->options[SERIAL_____FLAG])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add doSerial to LinkTestInfo dictionary."); + return -1; + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Do not store results in a SION file?" ),PyLong_FromLong((long int)linkTestInfo->options[SION_OUT___FLAG])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add doWrite to LinkTestInfo dictionary."); + return -1; + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Write SION output in parallel?" ),PyLong_FromLong((long int)linkTestInfo->options[SION_PAR___FLAG])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add doParallelIO to LinkTestInfo dictionary."); + return -1; + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Store messages in GPU memory?" ),PyLong_FromLong((long int)linkTestInfo->options[DEVICE_MEM_FLAG])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add useGPUs to LinkTestInfo dictionary."); + return -1; + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Use multiple memory buffers?" ),PyLong_FromLong((long int)linkTestInfo->options[MULTI_BUF__FLAG])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add use_multi_buf to LinkTestInfo dictionary."); + return -1; + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Randomize memory-buffer content?" ),PyLong_FromLong((long int)linkTestInfo->options[RAND_BUF___FLAG])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add randomize_buffers to LinkTestInfo dictionary."); + return -1; + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Check memory-buffer content?" ),PyLong_FromLong((long int)linkTestInfo->options[CHECK_BUF__FLAG])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add check_buffers to LinkTestInfo dictionary."); + return -1; + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Group processes according to hostname?" ),PyLong_FromLong((long int)linkTestInfo->options[GROUP_HOST_FLAG])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add check_buffers to LinkTestInfo dictionary."); + return -1; + } + switch(linkTestInfo->options[MEM_ALLOC__TYPE]){ + case AllocatorInvalid: + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Allocator type"),PyUnicode_FromString("Invalid - ERROR" )); + break; + case AllocatorDefault: + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Allocator type"),PyUnicode_FromString("Default - ERROR" )); + break; + case AllocatorAlignedMalloc: + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Allocator type"),PyUnicode_FromString("Memory-aligned malloc" )); + break; + case AllocatorPinnedMmap: + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Allocator type"),PyUnicode_FromString("Pinned memory-map" )); + break; + case AllocatorPOSIXAlignedMalloc: + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Allocator type"),PyUnicode_FromString("POSIX memory-aligned malloc")); + break; + case AllocatorCUDA: + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Allocator type"),PyUnicode_FromString("CUDA malloc" )); + break; + default: + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Allocator type"),PyUnicode_FromString("UNKNOWN" )); + break; + } + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add memory-buffer allocator to LinkTestInfo dictionary."); + return -1; + } + + /* Set Integer Information */ + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Number of messages" ),PyLong_FromLong((long int)linkTestInfo->iInfo[NUM_MESSAGES])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add numberOfMessages to LinkTestInfo dictionary."); + return -1; + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Message size" ),PyLong_FromLong((long int)linkTestInfo->iInfo[MESSAGE_SIZE])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add lengthOfMessages to LinkTestInfo dictionary."); + return -1; + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Number of warm-up messages" ),PyLong_FromLong((long int)linkTestInfo->iInfo[NUM_WARM__UP])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add numberOfWarmUpMessages to LinkTestInfo dictionary."); + return -1; + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Number of serial retests" ),PyLong_FromLong((long int)linkTestInfo->iInfo[NUM___SERIAL])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add numberOfSerialRetests to LinkTestInfo dictionary."); + return -1; + } + if(linkTestInfo->options[MULTI_BUF__FLAG]){ + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Number of multiple buffers" ),PyLong_FromLong((long int)linkTestInfo->iInfo[NUM__MEM_BUF])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add num_multi_buf to LinkTestInfo dictionary."); + return -1; + } + } + if(linkTestInfo->options[RAND_BUF___FLAG]){ + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Buffer Mersenne Twister seed" ),PyLong_FromLong((long int)linkTestInfo->iInfo[MEM_BUF_SEED])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add buf_mt_seed to LinkTestInfo dictionary."); + return -1; + } + } + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Number of tests with randomized rank order"),PyLong_FromLong((long int)linkTestInfo->iInfo[NUM_PERMUTAT])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add num_randomize_tasks to LinkTestInfo dictionary."); + return -1; + } + if(linkTestInfo->iInfo[NUM_PERMUTAT]){ + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Seed for rank-order randomization"),PyLong_FromLong((long int)linkTestInfo->iInfo[PERMUTA_SEED])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add seed_randomize_tasks to LinkTestInfo dictionary."); + return -1; + } + } + if(linkTestInfo->options[RAND_STEP__FLAG]){ + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Seed for step-order randomization"),PyLong_FromLong((long int)linkTestInfo->iInfo[STEP____SEED])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add seed_randomize_steps to LinkTestInfo dictionary."); + return -1; + } + } + + return 0; +} + +/*******************************/ +/* Read & Import LinkTest Data */ +/*******************************/ +int readImportData_2_2_0(struct SionFile* const fh, const int swap, struct LinkTestInfo_2_2_0* const linkTestInfo, PyObject* const* const pyLinkTestInfo, PyObject** pyLinkTestData){ + const size_t end_block_len=sizeof(END_BLOCK_2_2_0)/sizeof(char); + char end_block[end_block_len]; + int err; + int32_t rank, hostname_len; + size_t n; + size_t i; + char* hostname; + npy_intp len[2]; + void *ptr; + + const size_t num_iterations =(linkTestInfo->iInfo[NUM_PERMUTAT]>1)?(size_t)linkTestInfo->iInfo[NUM_PERMUTAT]:1; + const size_t numMinMaxAvgTimings=(linkTestInfo->options[A2A________FLAG]!=0)?6:3; + double minMaxAvgTimings[num_iterations*numMinMaxAvgTimings]; + + //TODO: Handle Errors + + /* Set Python Array Sizes */ + len[0] = fh->size; + len[1] = fh->size; + + /* Initialize Hostnames & Core IDs */ + PyObject* const pyHosts = PyList_New((Py_ssize_t)fh->size); + PyObject* const pyCore = NewNumericPyArray(NPY_INT, 1, len); + + /* Initialize Start & End Time Strings */ + PyObject** const pyStartTime=(PyObject**)malloc(num_iterations*sizeof(PyObject)); + for(i=0;i<num_iterations;i++) pyStartTime[i]=PyDict_New(); + PyObject** const pyEndTime =(PyObject**)malloc(num_iterations*sizeof(PyObject)); + for(i=0;i<num_iterations;i++) pyEndTime[i] =PyDict_New(); + + /* Initialize Timings, Access Pattern & All-To-All Timings */ + PyObject** const pyTimings =(PyObject**)malloc(num_iterations*sizeof(PyObject)); + for(i=0;i<num_iterations;i++) pyTimings[i] =NewNumericPyArray(NPY_DOUBLE,2,len); //A python double-precision floating-point array of the LinkTest point-to-point timings + PyObject** const pyAccesspattern=(PyObject**)malloc(num_iterations*sizeof(PyObject)); + for(i=0;i<num_iterations;i++) pyAccesspattern[i]=NewNumericPyArray(NPY_UINT64,2,len); //A python integer array indicating the access order + PyObject** const pyA2ATimings =(linkTestInfo->options[A2A________FLAG]!=0)?(PyObject**)malloc(num_iterations*sizeof(PyObject)):NULL; //Silences maybe-used-uninitialized warning + if(linkTestInfo->options[A2A________FLAG]!=0) for(i=0;i<num_iterations;i++) pyA2ATimings[i] =NewNumericPyArray(NPY_DOUBLE, 1, len); //A python double-precision floating-point array of the linkTest all-to-all imings + + /* Initialize Serial Retest Timings */ + len[0] = linkTestInfo->iInfo[NUM___SERIAL]; + PyObject** const pySerialTimingsNew=(len[0]>0)?(PyObject**)malloc(num_iterations*sizeof(PyObject)):NULL; + if(len[0]>0) for(i=0;i<num_iterations;i++) pySerialTimingsNew[i] = NewNumericPyArray(NPY_DOUBLE, 1, len); + PyObject** const pySerialTimingsOld=(len[0]>0)?(PyObject**)malloc(num_iterations*sizeof(PyObject)):NULL; + if(len[0]>0) for(i=0;i<num_iterations;i++) pySerialTimingsOld[i] = NewNumericPyArray(NPY_DOUBLE, 1, len); + PyObject** const pySerialFrom =(len[0]>0)?(PyObject**)malloc(num_iterations*sizeof(PyObject)):NULL; + if(len[0]>0) for(i=0;i<num_iterations;i++) pySerialFrom[i] = NewNumericPyArray(NPY_UINT64, 1, len); + PyObject** const pySerialTo =(len[0]>0)?(PyObject**)malloc(num_iterations*sizeof(PyObject)):NULL; + if(len[0]>0) for(i=0;i<num_iterations;i++) pySerialTo[i] = NewNumericPyArray(NPY_UINT64, 1, len); + + for (rank = 0; rank < fh->size; ++rank) { //Loop over the ranks, each one was saved to its own chunk + /* Read Host Name (Multiple Tasks May Have The Same Host Name */ + n=fread(&hostname_len, sizeof(uint32_t), 1, fh->fp); + if (UNLIKELY(n != 1)) { + PyErr_SetString(PyExc_RuntimeError, "readImportData_2_2_0: Failed to read/import hostname length."); + return -1; + } + sion_swap(&hostname_len, &hostname_len, sizeof(uint32_t), 1, swap); + hostname=(char*)malloc(hostname_len*sizeof(char)); + n=fread(hostname, sizeof(char), hostname_len, fh->fp); + if (UNLIKELY((int32_t)n != hostname_len)) { + PyErr_SetString(PyExc_RuntimeError, "readImportData_2_2_0: Failed to read/import Hostname."); + return -1; + } + sion_swap(&hostname, &hostname, sizeof(char), 1, swap); + #ifdef DEBUG_OUTPUT + printf("Hostname[%d] =%s\n",rank,hostname);fflush(stdout); + #endif + PyList_SET_ITEM(pyHosts,(Py_ssize_t)rank,PyBytes_FromStringAndSize(hostname,(Py_ssize_t)hostname_len)); + /* Read Pinned-Core ID */ + ptr=PyArray_GETPTR1((PyArrayObject *)pyCore, rank); + n=fread(ptr, sizeof(int32_t), 1, fh->fp); + if (UNLIKELY(n != 1)) { + PyErr_SetString(PyExc_RuntimeError, "readImportData_2_2_0: Failed to read/import Pinned Core ID."); + return -1; + } + sion_swap(ptr, ptr, sizeof(int32_t), 1, swap); + #ifdef DEBUG_OUTPUT + printf("cpuID[%d] =%d\n",rank,*(int32_t*)ptr);fflush(stdout); + #endif + + for(i=0;i<num_iterations;i++){ + if(rank==0){ + /* Read Start Time */ + char datetime_string[LINKTEST_OUTPUT_DATETIME_LENGTH_2_2_0+1]; //TODO: Dangerous if sizeof(char) mismatches between writing and reading system. + n=fread(datetime_string, sizeof(char), LINKTEST_OUTPUT_DATETIME_LENGTH_2_2_0, fh->fp); + if (UNLIKELY(n != LINKTEST_OUTPUT_DATETIME_LENGTH_2_2_0)) { + PyErr_SetString(PyExc_RuntimeError, "readInfo_2_2_0: Failed to read startDatetime."); + return -1; + } + sion_swap(datetime_string, datetime_string, sizeof(char), LINKTEST_OUTPUT_DATETIME_LENGTH_2_2_0, swap); + if (UNLIKELY(datetime_string[0] == '\0')){ + PyErr_SetString(PyExc_RuntimeError, "readInfo_2_2_0: startDatetime NULL is not supported."); + return -1; + } + datetime_string[LINKTEST_OUTPUT_DATETIME_LENGTH_2_2_0]='\0'; //Ensure string is null terminated + #ifdef DEBUG_OUTPUT + printf("startDatetime[0,%zu] =%s",i,datetime_string); + fflush(stdout); + #endif + struct Datetime startDatetime; + n=sscanf(datetime_string, + "%2"SCNu8"/%2"SCNu8"/%"SCNu64" %2"SCNu8":%2"SCNu8":%2"SCNu8" UTC+0", + &startDatetime.day, + &startDatetime.month, + &startDatetime.year, + &startDatetime.hour, + &startDatetime.minute, + &startDatetime.second + ); + if (UNLIKELY(n!=6)){ + PyErr_SetString(PyExc_RuntimeError, "readInfo_2_2_0: startDatetime is not a correctly formatted string."); + return -1; + } + #ifdef DEBUG_OUTPUT + printf("startDatetime.year[0,%zu] =%" PRIu64 "\n",i,startDatetime.year ); + printf("startDatetime.month[0,%zu] =%" PRIu8 "\n",i,startDatetime.month ); + printf("startDatetime.day[0,%zu] =%" PRIu8 "\n",i,startDatetime.day ); + printf("startDatetime.hour[0,%zu] =%" PRIu8 "\n",i,startDatetime.hour ); + printf("startDatetime.minute[0,%zu] =%" PRIu8 "\n",i,startDatetime.minute); + printf("startDatetime.second[0,%zu] =%" PRIu8 "\n",i,startDatetime.second); + printf("startDatetime.timezone[0,%zu]=UTC+0\n" ,i ); + fflush(stdout); + #endif + /* Import Start Time */ + err=PyDict_SetItem(pyStartTime[i],PyUnicode_FromString("Year" ),PyLong_FromLong((long int)startDatetime.year )); + if(err){//TODO: Add iteration index to error message + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add DateTime_year to pyStartTime dictionary."); + return -1; + } + err=PyDict_SetItem(pyStartTime[i],PyUnicode_FromString("Month" ),PyLong_FromLong((long int)startDatetime.month )); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add DateTime_month to pyStartTime dictionary."); + return -1; + } + err=PyDict_SetItem(pyStartTime[i],PyUnicode_FromString("Day" ),PyLong_FromLong((long int)startDatetime.day )); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add DateTime_day to pyStartTime dictionary."); + return -1; + } + err=PyDict_SetItem(pyStartTime[i],PyUnicode_FromString("Hour" ),PyLong_FromLong((long int)startDatetime.hour )); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add DateTime_hour to pyStartTime dictionary."); + return -1; + } + err=PyDict_SetItem(pyStartTime[i],PyUnicode_FromString("Minute" ),PyLong_FromLong((long int)startDatetime.minute)); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add DateTime_minute to pyStartTime dictionary."); + return -1; + } + err=PyDict_SetItem(pyStartTime[i],PyUnicode_FromString("Second" ),PyLong_FromLong((long int)startDatetime.second)); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add DateTime_second to pyStartTime dictionary."); + return -1; + } + err=PyDict_SetItem(pyStartTime[i],PyUnicode_FromString("Timezone"),PyUnicode_FromFormat("UTC+0")); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add DateTime_timezone to pyStartTime dictionary."); + return -1; + } + /* Read Minimum, Maximum & Average LinkTest Timings */ + n=fread(&minMaxAvgTimings[i*numMinMaxAvgTimings], sizeof(double), numMinMaxAvgTimings, fh->fp); + if (UNLIKELY(n != numMinMaxAvgTimings)) { + PyErr_SetString(PyExc_RuntimeError, "readInfo_2_2_0: Failed to read minimum, maximum & average LinkTest timings."); + return -1; + } + sion_swap(minMaxAvgTimings, minMaxAvgTimings, sizeof(double), numMinMaxAvgTimings, swap); + #ifdef DEBUG_OUTPUT + printf("min_time[0,%zu] =%e\n",i,minMaxAvgTimings[i*numMinMaxAvgTimings+0]); + printf("max_time[0,%zu] =%e\n",i,minMaxAvgTimings[i*numMinMaxAvgTimings+1]); + printf("avg_time[0,%zu] =%e\n",i,minMaxAvgTimings[i*numMinMaxAvgTimings+2]); + if(numMinMaxAvgTimings==6){ + printf("min_time_a2a[0,%zu] =%e\n",i,minMaxAvgTimings[i*numMinMaxAvgTimings+3]); + printf("max_time_a2a[0,%zu] =%e\n",i,minMaxAvgTimings[i*numMinMaxAvgTimings+4]); + printf("avg_time_a2a[0,%zu] =%e\n",i,minMaxAvgTimings[i*numMinMaxAvgTimings+5]); + } + fflush(stdout); + #endif + /* Read Serial Retest Data */ + if (linkTestInfo->iInfo[NUM___SERIAL]>0){ + /* Read New Serially Retested Timings */ + ptr=PyArray_GETPTR1((PyArrayObject *)pySerialTimingsNew[i], 0); + n=fread(ptr, sizeof(double ), linkTestInfo->iInfo[NUM___SERIAL], fh->fp); + if (UNLIKELY((uint64_t)n != linkTestInfo->iInfo[NUM___SERIAL])) { + PyErr_SetString(PyExc_RuntimeError, "readImportData_2_2_0: Failed to read/import retested serial timings."); + return -1; + } + sion_swap(ptr, ptr, sizeof(double ), linkTestInfo->iInfo[NUM___SERIAL], swap); + #ifdef DEBUG_OUTPUT + printf("serialTimingsNew[0,%zu] =%f",i,((double*)ptr)[0]); + for(n=1;(uint64_t)n<linkTestInfo->iInfo[NUM___SERIAL];n++) printf(",%f",((double*)ptr)[n]); + printf("\n"); + fflush(stdout); + #endif + /* Read Old Serially Retested Timings */ + ptr=PyArray_GETPTR1((PyArrayObject *)pySerialTimingsOld[i], 0); + n=fread(ptr, sizeof(double ), linkTestInfo->iInfo[NUM___SERIAL], fh->fp); + if (UNLIKELY((uint64_t)n != linkTestInfo->iInfo[NUM___SERIAL])) { + PyErr_SetString(PyExc_RuntimeError, "readImportData_2_2_0: Failed to read/import retest-needed timings."); + return -1; + } + sion_swap(ptr, ptr, sizeof(double ), linkTestInfo->iInfo[NUM___SERIAL], swap); + #ifdef DEBUG_OUTPUT + printf("serialTimingsOld[0,%zu] =%f",i,((double*)ptr)[0]); + for(n=1;(uint64_t)n<linkTestInfo->iInfo[NUM___SERIAL];n++) printf(",%f",((double*)ptr)[n]); + printf("\n"); + fflush(stdout); + #endif + /* Read Origin Partner For Serial Retests */ + ptr=PyArray_GETPTR1((PyArrayObject *)pySerialFrom[i] , 0); + n=fread(ptr, sizeof(uint64_t), linkTestInfo->iInfo[NUM___SERIAL], fh->fp); + if (UNLIKELY((uint64_t)n != linkTestInfo->iInfo[NUM___SERIAL])) { + PyErr_SetString(PyExc_RuntimeError, "readImportData_2_2_0: Failed to read/import serial retest origin."); + return -1; + } + #ifdef DEBUG_OUTPUT + printf("serialTimingsFrom[0,%zu] =%d",i,((uint64_t*)ptr)[0]); + for(n=1;(uint64_t)n<linkTestInfo->iInfo[NUM___SERIAL];n++) printf(",%"PRIu64,((uint64_t*)ptr)[n]); + printf("\n"); + fflush(stdout); + #endif + sion_swap(ptr, ptr, sizeof(uint64_t), linkTestInfo->iInfo[NUM___SERIAL], swap); + /* Read Destination Partner For Serial Retests */ + ptr=PyArray_GETPTR1((PyArrayObject *)pySerialTo[i] , 0); + n=fread(ptr, sizeof(uint64_t), linkTestInfo->iInfo[NUM___SERIAL], fh->fp); + if (UNLIKELY((uint64_t)n != linkTestInfo->iInfo[NUM___SERIAL])) { + PyErr_SetString(PyExc_RuntimeError, "readImportData_2_2_0: Failed to read/import serial retest destination."); + return -1; + } + sion_swap(ptr, ptr, sizeof(uint64_t), linkTestInfo->iInfo[NUM___SERIAL], swap); + #ifdef DEBUG_OUTPUT + printf("serialTimingsTo[0,%zu] =%d",i,((uint64_t*)ptr)[0]); + for(n=1;(uint64_t)n<linkTestInfo->iInfo[NUM___SERIAL];n++) printf(",%"PRIu64,((uint64_t*)ptr)[n]); + printf("\n"); + fflush(stdout); + #endif + } + /* Read End Time */ + n=fread(datetime_string, sizeof(char), LINKTEST_OUTPUT_DATETIME_LENGTH_2_2_0, fh->fp); + if (UNLIKELY(n != LINKTEST_OUTPUT_DATETIME_LENGTH_2_2_0)) { + PyErr_SetString(PyExc_RuntimeError, "readInfo_2_2_0: Failed to read endDatetime."); + return -1; + } + sion_swap(datetime_string, datetime_string, sizeof(char), LINKTEST_OUTPUT_DATETIME_LENGTH_2_2_0, swap); + if (UNLIKELY(datetime_string[0] == '\0')){ + PyErr_SetString(PyExc_RuntimeError, "readInfo_2_2_0: endDatetime NULL is not supported."); + return -1; + } + datetime_string[LINKTEST_OUTPUT_DATETIME_LENGTH_2_2_0]='\0'; //Ensure string is null terminated + #ifdef DEBUG_OUTPUT + printf("endDatetime[0,%zu] =%s",i,datetime_string); + fflush(stdout); + #endif + struct Datetime endDatetime; + n=sscanf(datetime_string, + "%2"SCNu8"/%2"SCNu8"/%"SCNu64" %2"SCNu8":%2"SCNu8":%2"SCNu8" UTC+0", + &endDatetime.day, + &endDatetime.month, + &endDatetime.year, + &endDatetime.hour, + &endDatetime.minute, + &endDatetime.second + ); + if (UNLIKELY(n!=6)){ + PyErr_SetString(PyExc_RuntimeError, "readInfo_2_2_0: endDatetime is not a correctly formatted string."); + return -1; + } + #ifdef DEBUG_OUTPUT + printf("endDatetime.year[0,%zu] =%" PRIu64 "\n",i,endDatetime.year ); + printf("endDatetime.month[0,%zu] =%" PRIu8 "\n",i,endDatetime.month ); + printf("endDatetime.day[0,%zu] =%" PRIu8 "\n",i,endDatetime.day ); + printf("endDatetime.hour[0,%zu] =%" PRIu8 "\n",i,endDatetime.hour ); + printf("endDatetime.minute[0,%zu] =%" PRIu8 "\n",i,endDatetime.minute); + printf("endDatetime.second[0,%zu] =%" PRIu8 "\n",i,endDatetime.second); + printf("endDatetime.timezone[0,%zu] =UTC+0\n" ,i ); + fflush(stdout); + #endif + /* Import End Time */ + err=PyDict_SetItem(pyEndTime[i],PyUnicode_FromString("Year" ),PyLong_FromLong((long int)endDatetime.year )); + if(err){//TODO: Add iteration index to error message + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add DateTime_year to pyEndTime dictionary."); + return -1; + } + err=PyDict_SetItem(pyEndTime[i],PyUnicode_FromString("Month" ),PyLong_FromLong((long int)endDatetime.month )); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add DateTime_month to pyEndTime dictionary."); + return -1; + } + err=PyDict_SetItem(pyEndTime[i],PyUnicode_FromString("Day" ),PyLong_FromLong((long int)endDatetime.day )); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add DateTime_day to pyEndTime dictionary."); + return -1; + } + err=PyDict_SetItem(pyEndTime[i],PyUnicode_FromString("Hour" ),PyLong_FromLong((long int)endDatetime.hour )); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add DateTime_hour to pyEndTime dictionary."); + return -1; + } + err=PyDict_SetItem(pyEndTime[i],PyUnicode_FromString("Minute" ),PyLong_FromLong((long int)endDatetime.minute)); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add DateTime_minute to pyEndTime dictionary."); + return -1; + } + err=PyDict_SetItem(pyEndTime[i],PyUnicode_FromString("Second" ),PyLong_FromLong((long int)endDatetime.second)); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add DateTime_second to pyEndTime dictionary."); + return -1; + } + err=PyDict_SetItem(pyEndTime[i],PyUnicode_FromString("Timezone"),PyUnicode_FromFormat("UTC+0")); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add DateTime_timezone to pyEndTime dictionary."); + return -1; + } + } + /* Read LinkTest Timings */ + ptr=PyArray_GETPTR2((PyArrayObject *)pyTimings[i] , rank, 0); + n=fread(ptr, sizeof(double), fh->size, fh->fp); + if (UNLIKELY((int32_t)n != fh->size)) { + PyErr_SetString(PyExc_RuntimeError, "readImportData_2_2_0: Failed to read/import LinkTest timings."); + return -1; + } + sion_swap(ptr, ptr, sizeof(double), fh->size, swap); + #ifdef DEBUG_OUTPUT + printf("timings[%d,%zu] =%e",rank,i,((double*)ptr)[0]); + for(n=1;(int32_t)n<fh->size;n++) printf(",%e",((double*)ptr)[n]); + printf("\n"); + fflush(stdout); + #endif + /* Read LinkTest Access Pattern */ + ptr=PyArray_GETPTR2((PyArrayObject *)pyAccesspattern[i], rank, 0); + n=fread(ptr, sizeof(int64_t), fh->size, fh->fp); + if (UNLIKELY((int64_t)n != fh->size)) { + PyErr_SetString(PyExc_RuntimeError, "readImportData_2_2_0: Failed to read/import LinkTest access pattern."); + return -1; + } + sion_swap(ptr, ptr, sizeof(uint64_t), fh->size, swap); + #ifdef DEBUG_OUTPUT + printf("accesspattern[%d,%zu] =%" PRIu64,rank,i,((uint64_t*)ptr)[0]); + for(n=1;(int32_t)n<fh->size;n++) printf(",%" PRIu64,((uint64_t*)ptr)[n]); + printf("\n"); + fflush(stdout); + #endif + /* Read LinkTest All-To-All Timings */ + if (linkTestInfo->options[A2A________FLAG]){ + ptr=PyArray_GETPTR1((PyArrayObject *)pyA2ATimings[i], rank); + n=fread(ptr, sizeof(double), 1, fh->fp); + if (UNLIKELY(n != 1)) { + PyErr_SetString(PyExc_RuntimeError, "readImportData_2_2_0: Failed to read/import all-to-all timing."); + return -1; + } + sion_swap(ptr, ptr, sizeof(double), 1, swap); + #ifdef DEBUG_OUTPUT + printf("all-to-all time[%d,%zu] =%f\n",rank,i,((double*)ptr)[0]);fflush(stdout); + #endif + } + } + + /* Check Alignment by Testing for END_BLOCK */ + n=fread(end_block,sizeof(char),end_block_len-1,fh->fp); + if (UNLIKELY(n != end_block_len-1)){ + PyErr_SetString(PyExc_RuntimeError, "readImportData_2_2_0: Failed to read END_BLOCK string."); + return -1; + } + end_block[end_block_len-1]='\0'; + if(UNLIKELY(strcmp(end_block,END_BLOCK_2_2_0))){ + PyErr_SetString(PyExc_RuntimeError, "readImportData_2_2_0: Alignment Error Detected. \"END_BLOCK\" not found at the correct location."); + return -1; + } + } + + /* Close SION File */ + sion_close(fh->sid); + + /* Add Hostname To LinkTestInfo Dictionary */ + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Hosts"),pyHosts); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add Hosts to LinkTestInfo dictionary."); + return -1; + } + + /* Add Core ID To LinkTestInfo Dictionary */ + err=PyDict_SetItem(*pyLinkTestInfo,PyUnicode_FromString("Core ID"),pyCore); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to add Core ID to LinkTestInfo dictionary."); + return -1; + } + + /* Create Data Dictionaries */ + PyObject** const pyLinkTestDataDicts=(PyObject**)malloc(num_iterations*sizeof(PyObject)); + for(i=0;i<num_iterations;i++) pyLinkTestDataDicts[i] = PyDict_New(); + + /* Fill Dictionaries */ + for(i=0;i<num_iterations;i++){ + err=PyDict_SetItem(pyLinkTestDataDicts[i], PyUnicode_FromString("Start Time" ), pyStartTime[i] ); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"readImportData_2_2_0: Failed to add start time to LinkTestData dictionary."); + return -1; + } + /* Set Double-Precision Floating-Point Information */ + err=PyDict_SetItem(pyLinkTestDataDicts[i],PyUnicode_FromString("Minimum time" ),PyFloat_FromDouble(minMaxAvgTimings[i*numMinMaxAvgTimings+0])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_1_2: Failed to add minTimeAll to LinkTestData dictionary."); + return -1; + } + err=PyDict_SetItem(pyLinkTestDataDicts[i],PyUnicode_FromString("Maximum time" ),PyFloat_FromDouble(minMaxAvgTimings[i*numMinMaxAvgTimings+1])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_1_2: Failed to add maxTimeAll to LinkTestData dictionary."); + return -1; + } + err=PyDict_SetItem(pyLinkTestDataDicts[i],PyUnicode_FromString("Average time" ),PyFloat_FromDouble(minMaxAvgTimings[i*numMinMaxAvgTimings+2])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_1_2: Failed to add avgTimeAll to LinkTestData dictionary."); + return -1; + } + if(numMinMaxAvgTimings==6){ //Only execute if all-to-all testing was performed + err=PyDict_SetItem(pyLinkTestDataDicts[i],PyUnicode_FromString("Minimum all-to-all time"),PyFloat_FromDouble(minMaxAvgTimings[i*numMinMaxAvgTimings+3])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_1_2: Failed to add a2aMinTimeAll to LinkTestData dictionary."); + return -1; + } + err=PyDict_SetItem(pyLinkTestDataDicts[i],PyUnicode_FromString("Maximum all-to-all time"),PyFloat_FromDouble(minMaxAvgTimings[i*numMinMaxAvgTimings+4])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_1_2: Failed to add a2aMaxTimeAll to LinkTestData dictionary."); + return -1; + } + err=PyDict_SetItem(pyLinkTestDataDicts[i],PyUnicode_FromString("Average all-to-all time"),PyFloat_FromDouble(minMaxAvgTimings[i*numMinMaxAvgTimings+5])); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"importInfo_2_1_2: Failed to add a2aAvgTimeAll to LinkTestData dictionary."); + return -1; + } + } + + err=PyDict_SetItem(pyLinkTestDataDicts[i], PyUnicode_FromString("Timings" ), pyTimings[i] ); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"readImportData_2_2_0: Failed to add timings to LinkTestData dictionary."); + return -1; + } + err=PyDict_SetItem(pyLinkTestDataDicts[i], PyUnicode_FromString("Access Pattern"), pyAccesspattern[i]); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"readImportData_2_2_0: Failed to add accessPatterns to LinkTestData dictionary."); + return -1; + } + if(linkTestInfo->iInfo[NUM___SERIAL]>0){ + err=PyDict_SetItem(pyLinkTestDataDicts[i], PyUnicode_FromString("New serially retested timings" ), pySerialTimingsNew[i]); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"readImportData_2_2_0: Failed to add serialTimingsNew to LinkTestData dictionary."); + return -1; + } + err=PyDict_SetItem(pyLinkTestDataDicts[i], PyUnicode_FromString("Old serially retested timings" ), pySerialTimingsOld[i]); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"readImportData_2_2_0: Failed to add serialTimingsOld to LinkTestData dictionary."); + return -1; + } + err=PyDict_SetItem(pyLinkTestDataDicts[i], PyUnicode_FromString("Serial-retest origin hosts" ), pySerialFrom[i]); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"readImportData_2_2_0: Failed to add serialFrom to LinkTestData dictionary."); + return -1; + } + err=PyDict_SetItem(pyLinkTestDataDicts[i], PyUnicode_FromString("Serial-retest destination hosts"), pySerialTo[i]); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"readImportData_2_2_0: Failed to add serialTo to LinkTestData dictionary."); + return -1; + } + } + if(linkTestInfo->options[A2A________FLAG]){ + err=PyDict_SetItem(pyLinkTestDataDicts[i], PyUnicode_FromString("All-to-all timings"), pyA2ATimings[i]); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"readImportData_2_2_0: Failed to add a2aTimings to LinkTestData dictionary."); + return -1; + } + } + err=PyDict_SetItem(pyLinkTestDataDicts[i], PyUnicode_FromString("End Time" ), pyEndTime[i] ); + if(err){ + PyErr_SetString(PyExc_RuntimeError,"readImportData_2_2_0: Failed to add end time to LinkTestData dictionary."); + return -1; + } + } + + /* Create Python List If Necessary */ + if(num_iterations>1){ + *pyLinkTestData=PyList_New(num_iterations); + for(i=0;i<num_iterations;i++)PyList_SET_ITEM(*pyLinkTestData,i,pyLinkTestDataDicts[i]); + }else{ + *pyLinkTestData=pyLinkTestDataDicts[0]; + } + + /* Free Memory */ + free(pyTimings ); + free(pyAccesspattern); + if(pyA2ATimings!=NULL) free(pyA2ATimings); + if(linkTestInfo->iInfo[NUM___SERIAL]>0){ + free(pySerialTimingsNew); + free(pySerialTimingsOld); + free(pySerialFrom ); + free(pySerialTo ); + } + + return 0; +} + +/**********************/ +/* Read & Import Data */ +/**********************/ +int readImport_2_2_0(struct SionFile* const fh, const int swap, const Version version, PyObject** const pyLinkTestInfo, PyObject** const pyLinkTestData){ + struct LinkTestInfo_2_2_0 linkTestInfo; + int err; + /* Read LinkTest Information */ + err = readInfo_2_2_0(fh, swap, version, &linkTestInfo); + if(err){ +// PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to read LinkTest information."); + return err; + } + /* Import LinkTest Information Into Python */ + *pyLinkTestInfo = PyDict_New(); //Create LinkTest information dictionary + err = importInfo_2_2_0(version, &linkTestInfo, pyLinkTestInfo); + if(err){ +// PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to populate the LinkTestInfo dictionary."); + //TODO: Safely Deallocate Python Dictionary + return err; + } + /* Read & Import LinkTest Data Into Python */ + err = readImportData_2_2_0(fh, swap, &linkTestInfo, pyLinkTestInfo, pyLinkTestData); + if(err){ +// PyErr_SetString(PyExc_RuntimeError,"importInfo_2_2_0: Failed to read LinkTest data or to populate the LinkTestData dictionary."); + //TODO: Safely Deallocate Python Dictionary + return err; + } + + return(0); +} diff --git a/python/linktest/linktest_2_2_0.h b/python/linktest/linktest_2_2_0.h new file mode 100644 index 0000000000000000000000000000000000000000..7a0b19a8167d0d795a1cf38df6792472c82d647e --- /dev/null +++ b/python/linktest/linktest_2_2_0.h @@ -0,0 +1,13 @@ +/**************************************************************************** +** LinkTest ** +***************************************************************************** +** Copyright (c) 2008-2025 ** +** Forschungszentrum Juelich, Juelich Supercomputing Centre ** +** ** +** See the file COPYRIGHT in the package base directory for details ** +****************************************************************************/ + +/**********************/ +/* Read & Import Data */ +/**********************/ +int readImport_2_2_0(struct SionFile* const fh, const int swap, const Version version, PyObject** const pyLinkTestInfo, PyObject** const pyLinkTestData); diff --git a/python/linktest/report.py b/python/linktest/report.py index 4374e386ff41de09b1a358158e393f6ebc64c027..f9ca30f18e1cb1709bc22f919153368051c2f593 100644 --- a/python/linktest/report.py +++ b/python/linktest/report.py @@ -93,7 +93,9 @@ def find_renderer(fig): #print_figure() method.) import io fig.canvas.print_figure(io.BytesIO()) - renderer = fig._cachedRenderer + #_cachedRenderer does not seem to work anymore + #renderer = fig._cachedRenderer + renderer = fig._get_renderer() return(renderer) def max_bbox_extend_1pt(strings,fast): @@ -1126,8 +1128,9 @@ def linktest_report(metaData, time1=time(); # Check version - if metaData["Version"]!="2.0.0" and metaData["Version"]!="2.1.1" and metaData["Version"]!="2.1.2" and metaData["Version"]!="2.1.4" and metaData["Version"]!="2.1.5" and metaData["Version"]!="2.1.6" and metaData["Version"]!="2.1.7" and metaData["Version"]!="2.1.8" and metaData["Version"]!="2.1.9" and metaData["Version"]!="2.1.10" and metaData["Version"]!="2.1.11" and metaData["Version"]!="2.1.12" and metaData["Version"]!="2.1.13" and metaData["Version"]!="2.1.14" and metaData["Version"]!="2.1.15" and metaData["Version"]!="2.1.16" and metaData["Version"]!="2.1.17" and metaData["Version"]!="2.1.18" and metaData["Version"]!="2.1.19": - print(f"Potentially unsupported LinkTest version output detected.\nThe Supported LinkTest versions are 2.0.0 and 2.1.[1,2,4-19] while this file comes from {metaData['Version']}") + supported_versions = ["2.0.0"] + [f"2.1.{y}" for y in [x for x in range(1,3)] + [x for x in range(4,20)]] + ["2.2.0"] + if metaData["Version"] not in supported_versions: + print(f"Potentially unsupported LinkTest version output detected.\nThe Supported LinkTest versions are 2.0.0, 2.1.[1,2,4-19] and 2.2.0 while this file comes from {metaData['Version']}") if "Do a bisection test?" in metaData and metaData["Do a bisection test?"]!=0 and (metaData["Version"]=="2.0.0" or metaData["Version"]=="2.1.1" or metaData["Version"]=="2.1.2" or metaData["Version"]=="2.1.4" or metaData["Version"]=="2.1.5" or metaData["Version"]=="2.1.6" or metaData["Version"]=="2.1.7" or metaData["Version"]=="2.1.8" or metaData["Version"]=="2.1.9"): print("Bisection runs were not properly executed prior to LinkTest version 2.1.10. The Report may be faulty."); diff --git a/python/setup.py b/python/setup.py index b201a691585b9dddb9a8209de0ba718e1f0a6885..72cd56409ebd79cf0399e7037370a98d413da1bf 100644 --- a/python/setup.py +++ b/python/setup.py @@ -12,7 +12,7 @@ import codecs from setuptools import setup, Extension # Hard-coded LinkTest version for older pip installers -link_ver="2.1.19" +link_ver="2.2.0" # Hard-coded LinkTest git hash for older pip installers link_hash="UNKNOWN" link_hash_short="UNKNOWN" @@ -85,7 +85,8 @@ ext_modules=[ "linktest/linktest_2_1_16.c", "linktest/linktest_2_1_17.c", "linktest/linktest_2_1_18.c", - "linktest/linktest_2_1_19.c"], + "linktest/linktest_2_1_19.c", + "linktest/linktest_2_2_0.c"], include_dirs=['linktest'], extra_compile_args = sionlib_compile_args, #Add `+['-DDEBUG_OUTPUT']` to turn on debug output extra_link_args = sionlib_link_args