From 1d610267bfc1b601451af882259df471e1b59eee Mon Sep 17 00:00:00 2001
From: William F Godoy <williamfgc@yahoo.com>
Date: Mon, 17 Jul 2017 17:55:55 -0400
Subject: [PATCH] Moved profiling.log to profiling.json format Added test for
 generating profiling.json Fixed inclusion for NLohmannJson json.hpp in Test
 Tested resulting profiling.json on R (jsonlite), Python (json) and C++ (json
 for modern C++)

---
 source/adios2/engine/bp/BPFileWriter.cpp      |  53 ++++---
 source/adios2/engine/bp/BPFileWriter.h        |   4 +
 .../toolkit/format/bp1/BP1Aggregator.cpp      |  41 ++---
 .../adios2/toolkit/format/bp1/BP1Aggregator.h |   2 +-
 .../adios2/toolkit/format/bp1/BP1Writer.cpp   |  29 ++--
 source/adios2/toolkit/format/bp1/BP1Writer.h  |   9 +-
 testing/adios2/engine/CMakeLists.txt          |   4 +-
 testing/adios2/engine/bp/CMakeLists.txt       |  30 ++--
 .../engine/bp/TestBPWriteProfilingJSON.cpp    | 140 ++++++++++++++++++
 9 files changed, 223 insertions(+), 89 deletions(-)
 create mode 100644 testing/adios2/engine/bp/TestBPWriteProfilingJSON.cpp

diff --git a/source/adios2/engine/bp/BPFileWriter.cpp b/source/adios2/engine/bp/BPFileWriter.cpp
index c8e6fcef4..a37b69c68 100644
--- a/source/adios2/engine/bp/BPFileWriter.cpp
+++ b/source/adios2/engine/bp/BPFileWriter.cpp
@@ -71,37 +71,12 @@ void BPFileWriter::Close(const int transportIndex)
                                    m_BP1Writer.m_HeapBuffer.m_DataPosition,
                                    transportIndex);
 
-    // close them
     m_TransportsManager.CloseFiles(transportIndex);
 
-    if (m_BP1Writer.m_Profiler.IsActive)
+    if (m_BP1Writer.m_Profiler.IsActive &&
+        m_TransportsManager.AllTransportsClosed())
     {
-        // aggregate and write profiling.log
-        if (m_TransportsManager.AllTransportsClosed())
-        {
-            auto transportTypes = m_TransportsManager.GetTransportsTypes();
-            auto transportProfilers =
-                m_TransportsManager.GetTransportsProfilers();
-
-            const std::string log(m_BP1Writer.GetRankProfilingLog(
-                transportTypes, transportProfilers));
-            // TODO profiling.log from rank0
-
-            const std::string profilingLog(
-                m_BP1Writer.AggregateProfilingLog(log));
-
-            if (m_BP1Writer.m_BP1Aggregator.m_RankMPI == 0)
-            {
-                transport::FileStream profilingLogStream(m_MPIComm,
-                                                         m_DebugMode);
-                auto bpBaseNames = m_BP1Writer.GetBPBaseNames({m_Name});
-                profilingLogStream.Open(bpBaseNames[0] + "/profiling.log",
-                                        OpenMode::Write);
-                profilingLogStream.Write(profilingLog.c_str(),
-                                         profilingLog.size());
-                profilingLogStream.Close();
-            }
-        }
+        WriteProfilingJSONFile();
     }
 }
 
@@ -147,4 +122,26 @@ void BPFileWriter::InitBPBuffer()
     }
 }
 
+void BPFileWriter::WriteProfilingJSONFile()
+{
+    auto transportTypes = m_TransportsManager.GetTransportsTypes();
+    auto transportProfilers = m_TransportsManager.GetTransportsProfilers();
+
+    const std::string lineJSON(
+        m_BP1Writer.GetRankProfilingJSON(transportTypes, transportProfilers));
+
+    const std::string profilingJSON(
+        m_BP1Writer.AggregateProfilingJSON(lineJSON));
+
+    if (m_BP1Writer.m_BP1Aggregator.m_RankMPI == 0)
+    {
+        transport::FileStream profilingJSONStream(m_MPIComm, m_DebugMode);
+        auto bpBaseNames = m_BP1Writer.GetBPBaseNames({m_Name});
+        profilingJSONStream.Open(bpBaseNames[0] + "/profiling.json",
+                                 OpenMode::Write);
+        profilingJSONStream.Write(profilingJSON.c_str(), profilingJSON.size());
+        profilingJSONStream.Close();
+    }
+}
+
 } // end namespace adios
diff --git a/source/adios2/engine/bp/BPFileWriter.h b/source/adios2/engine/bp/BPFileWriter.h
index e38cd1050..bb7d55265 100644
--- a/source/adios2/engine/bp/BPFileWriter.h
+++ b/source/adios2/engine/bp/BPFileWriter.h
@@ -77,6 +77,10 @@ private:
      */
     template <class T>
     void DoWriteCommon(Variable<T> &variable, const T *values);
+
+    /** Write a profiling.json file from m_BP1Writer and m_TransportsManager
+     * profilers*/
+    void WriteProfilingJSONFile();
 };
 
 } // end namespace adios
diff --git a/source/adios2/toolkit/format/bp1/BP1Aggregator.cpp b/source/adios2/toolkit/format/bp1/BP1Aggregator.cpp
index 9c63f7258..4962e7007 100644
--- a/source/adios2/toolkit/format/bp1/BP1Aggregator.cpp
+++ b/source/adios2/toolkit/format/bp1/BP1Aggregator.cpp
@@ -33,9 +33,9 @@ BP1Aggregator::BP1Aggregator(MPI_Comm mpiComm, const bool debugMode)
     MPI_Comm_size(m_MPIComm, &m_SizeMPI);
 }
 
-std::string BP1Aggregator::GetGlobalProfilingLog(const std::string &rankLog)
+std::string BP1Aggregator::GetGlobalProfilingJSON(const std::string &rankLog)
 {
-    std::string profilingLog;
+    std::string profilingJSON;
 
     if (m_RankMPI == 0)
     {
@@ -80,38 +80,17 @@ std::string BP1Aggregator::GetGlobalProfilingLog(const std::string &rankLog)
 
         // write global string
         // key is to reserve memory first
-        profilingLog.reserve(rankLog.size() * m_SizeMPI);
+        profilingJSON.reserve(rankLog.size() * m_SizeMPI);
 
-        profilingLog += "{\n";
-        profilingLog += rankLog + ",\n";
+        profilingJSON += "[\n";
+        profilingJSON += rankLog;
         for (unsigned int i = 1; i < sizeMPI; ++i)
         {
-            const std::string rankLogStr(rankLogs[i - 1].data(),
-                                         rankLogs[i - 1].size());
-            profilingLog += rankLogStr + ",";
-            if (i < sizeMPI - 1)
-            {
-                profilingLog += "\n";
-            }
+            profilingJSON += ",\n";
+            profilingJSON.append(rankLogs[i - 1].data(),
+                                 rankLogs[i - 1].size());
         }
-        profilingLog.pop_back(); // eliminate trailing comma
-        profilingLog += "\n";
-        profilingLog += "}\n";
-
-        //        // write to file
-        //        std::ofstream logStream(fileName);
-        //        if (m_DebugMode)
-        //        {
-        //            if (!logStream)
-        //            {
-        //                throw std::ios_base::failure(
-        //                    "ERROR: couldn't open profiling file " + fileName
-        //                    + "\n");
-        //            }
-        //        }
-        //
-        //        logStream.write(logFile.c_str(), logFile.size());
-        //        logStream.close();
+        profilingJSON += "\n]\n"; // close json
     }
     else
     {
@@ -127,7 +106,7 @@ std::string BP1Aggregator::GetGlobalProfilingLog(const std::string &rankLog)
 
     MPI_Barrier(m_MPIComm); // Barrier here?
 
-    return profilingLog;
+    return profilingJSON;
 }
 
 } // end namespace format
diff --git a/source/adios2/toolkit/format/bp1/BP1Aggregator.h b/source/adios2/toolkit/format/bp1/BP1Aggregator.h
index 854a66d0c..f18dcf47b 100644
--- a/source/adios2/toolkit/format/bp1/BP1Aggregator.h
+++ b/source/adios2/toolkit/format/bp1/BP1Aggregator.h
@@ -46,7 +46,7 @@ public:
      * python dictionary format
      * @param rankLog contain rank profiling info to be aggregated
      */
-    std::string GetGlobalProfilingLog(const std::string &rankLog);
+    std::string GetGlobalProfilingJSON(const std::string &rankLog);
 
 private:
     const bool m_DebugMode = false;
diff --git a/source/adios2/toolkit/format/bp1/BP1Writer.cpp b/source/adios2/toolkit/format/bp1/BP1Writer.cpp
index da9361fc8..c7b9210b3 100644
--- a/source/adios2/toolkit/format/bp1/BP1Writer.cpp
+++ b/source/adios2/toolkit/format/bp1/BP1Writer.cpp
@@ -182,36 +182,39 @@ void BP1Writer::Close() noexcept
     }
 }
 
-std::string BP1Writer::GetRankProfilingLog(
+std::string BP1Writer::GetRankProfilingJSON(
     const std::vector<std::string> &transportsTypes,
     const std::vector<profiling::IOChrono *> &transportsProfilers) noexcept
 {
     auto lf_WriterTimer = [](std::string &rankLog,
                              const profiling::Timer &timer) {
-        rankLog += "'" + timer.m_Process + "_" + timer.GetShortUnits() + "': " +
-                   std::to_string(timer.m_ProcessTime) + ", ";
+        rankLog += "\"" + timer.m_Process + "_" + timer.GetShortUnits() +
+                   "\": " + std::to_string(timer.m_ProcessTime) + ", ";
     };
 
     // prepare string dictionary per rank
-    std::string rankLog("'rank_" + std::to_string(m_BP1Aggregator.m_RankMPI) +
-                        "': { ");
+    std::string rankLog("{ \"rank\": " +
+                        std::to_string(m_BP1Aggregator.m_RankMPI) + ", ");
 
     auto &profiler = m_Profiler;
 
     std::string timeDate(profiler.Timers.at("buffering").m_LocalTimeDate);
     timeDate.pop_back();
+    // avoid whitespace
+    std::replace(timeDate.begin(), timeDate.end(), ' ', '_');
 
-    rankLog += "'date_and_time': '" + timeDate + "', 'threads': " +
-               std::to_string(m_Threads) + ", 'bytes': " +
-               std::to_string(profiler.Bytes.at("buffering")) + ", ";
+    rankLog += "\"start\": \"" + timeDate + "\", ";
+    rankLog += "\"threads\": " + std::to_string(m_Threads) + ", ";
+    rankLog +=
+        "\"bytes\": " + std::to_string(profiler.Bytes.at("buffering")) + ", ";
     lf_WriterTimer(rankLog, profiler.Timers.at("buffering"));
 
     const size_t transportsSize = transportsTypes.size();
 
     for (unsigned int t = 0; t < transportsSize; ++t)
     {
-        rankLog += "'transport_" + std::to_string(t) + "': { ";
-        rankLog += "'type': '" + transportsTypes[t] + "', ";
+        rankLog += "\"transport_" + std::to_string(t) + "\": { ";
+        rankLog += "\"type\": \"" + transportsTypes[t] + "\", ";
 
         for (const auto &transportTimerPair : transportsProfilers[t]->Timers)
         {
@@ -231,15 +234,15 @@ std::string BP1Writer::GetRankProfilingLog(
             rankLog += "},";
         }
     }
-    rankLog += " }";
+    rankLog += " }"; // end rank entry
 
     return rankLog;
 }
 
 std::string
-BP1Writer::AggregateProfilingLog(const std::string &rankProfilingLog) noexcept
+BP1Writer::AggregateProfilingJSON(const std::string &rankProfilingLog) noexcept
 {
-    return m_BP1Aggregator.GetGlobalProfilingLog(rankProfilingLog);
+    return m_BP1Aggregator.GetGlobalProfilingJSON(rankProfilingLog);
 }
 
 // PRIVATE FUNCTIONS
diff --git a/source/adios2/toolkit/format/bp1/BP1Writer.h b/source/adios2/toolkit/format/bp1/BP1Writer.h
index 1a35644b2..44d306c3f 100644
--- a/source/adios2/toolkit/format/bp1/BP1Writer.h
+++ b/source/adios2/toolkit/format/bp1/BP1Writer.h
@@ -86,12 +86,17 @@ public:
      * @param transportsTypes list of transport types
      * @param transportsProfilers list of references to transport profilers
      */
-    std::string GetRankProfilingLog(
+    std::string GetRankProfilingJSON(
         const std::vector<std::string> &transportsTypes,
         const std::vector<profiling::IOChrono *> &transportsProfilers) noexcept;
 
+    /**
+     * Forms the final profiling.json string aggregating from all ranks
+     * @param rankProfilingJSON
+     * @return profiling.json
+     */
     std::string
-    AggregateProfilingLog(const std::string &rankProfilingLog) noexcept;
+    AggregateProfilingJSON(const std::string &rankProfilingJSON) noexcept;
 
 private:
     /** BP format version */
diff --git a/testing/adios2/engine/CMakeLists.txt b/testing/adios2/engine/CMakeLists.txt
index 1992d27f7..1fac5d460 100644
--- a/testing/adios2/engine/CMakeLists.txt
+++ b/testing/adios2/engine/CMakeLists.txt
@@ -5,9 +5,7 @@
 
 # We currently require ADIOS1 to test ADIOS v2 bp functionality since the read
 # API is not available yet
-if(ADIOS2_HAVE_ADIOS1)
-  add_subdirectory(bp)
-endif()
+add_subdirectory(bp)
 
 if(ADIOS2_HAVE_ADIOS1)
   add_subdirectory(adios1)
diff --git a/testing/adios2/engine/bp/CMakeLists.txt b/testing/adios2/engine/bp/CMakeLists.txt
index d1d99e71e..3153fd2ab 100644
--- a/testing/adios2/engine/bp/CMakeLists.txt
+++ b/testing/adios2/engine/bp/CMakeLists.txt
@@ -5,19 +5,27 @@
 
 # MPI versions of the test are not properly implemented at the moment
 if(NOT ADIOS2_HAVE_MPI)
-  find_package(ADIOS1 COMPONENTS sequential REQUIRED)
-
-  add_executable(TestBPWriteRead TestBPWriteRead.cpp)
-  target_link_libraries(TestBPWriteRead adios2 gtest adios1::adios)
   
-  add_executable(TestBPWriteReadstdio TestBPWriteReadstdio.cpp)
-  target_link_libraries(TestBPWriteReadstdio adios2 gtest adios1::adios)
+  if(ADIOS2_HAVE_ADIOS1)
+    find_package(ADIOS1 COMPONENTS sequential REQUIRED)
 
-  add_executable(TestBPWriteReadfstream TestBPWriteReadfstream.cpp)
-  target_link_libraries(TestBPWriteReadfstream adios2 gtest adios1::adios)
+    add_executable(TestBPWriteRead TestBPWriteRead.cpp)
+    target_link_libraries(TestBPWriteRead adios2 gtest adios1::adios)
   
+    add_executable(TestBPWriteReadstdio TestBPWriteReadstdio.cpp)
+    target_link_libraries(TestBPWriteReadstdio adios2 gtest adios1::adios)
 
-  gtest_add_tests(TARGET TestBPWriteRead)
-  gtest_add_tests(TARGET TestBPWriteReadstdio)
-  gtest_add_tests(TARGET TestBPWriteReadfstream)
+    add_executable(TestBPWriteReadfstream TestBPWriteReadfstream.cpp)
+    target_link_libraries(TestBPWriteReadfstream adios2 gtest adios1::adios)
+    
+    gtest_add_tests(TARGET TestBPWriteRead)
+    gtest_add_tests(TARGET TestBPWriteReadstdio)
+    gtest_add_tests(TARGET TestBPWriteReadfstream)
+    
+  endif()
+    
+  add_executable(TestBPWriteProfilingJSON TestBPWriteProfilingJSON.cpp)
+  target_link_libraries(TestBPWriteProfilingJSON adios2 gtest gtest_main NLohmannJson)
+  
+  gtest_add_tests(TARGET TestBPWriteProfilingJSON)
 endif()
diff --git a/testing/adios2/engine/bp/TestBPWriteProfilingJSON.cpp b/testing/adios2/engine/bp/TestBPWriteProfilingJSON.cpp
new file mode 100644
index 000000000..501062dbc
--- /dev/null
+++ b/testing/adios2/engine/bp/TestBPWriteProfilingJSON.cpp
@@ -0,0 +1,140 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * TestBPWriteProfilingJSON.cpp
+ *
+ *  Created on: Jul 18, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+
+#include <cstdint>
+#include <cstring>
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+
+#include <adios2.h>
+
+#include <gtest/gtest.h>
+#include <json.hpp> //This fails to be included
+
+#include "../SmallTestData.h"
+
+using json = nlohmann::json;
+
+class BPWriteProfilingJSONTest : public ::testing::Test
+{
+public:
+    BPWriteProfilingJSONTest() = default;
+
+    SmallTestData m_TestData;
+};
+
+//******************************************************************************
+// 1D 1x8 test data
+//******************************************************************************
+
+// ADIOS2 write, native ADIOS1 read
+TEST_F(BPWriteProfilingJSONTest, ADIOS2BPWriteProfilingJSON)
+{
+    std::string fname = "ADIOS2BPWriteProfilingJSON.bp";
+
+    // Write test data and profiling.json using ADIOS2
+    {
+        adios2::ADIOS adios(true);
+        adios2::IO &io = adios.DeclareIO("TestIO");
+
+        // Declare 1D variables
+        {
+            auto &var_i8 =
+                io.DefineVariable<char>("i8", {}, {}, adios2::Dims{8});
+            auto &var_i16 =
+                io.DefineVariable<short>("i16", {}, {}, adios2::Dims{8});
+            auto &var_i32 =
+                io.DefineVariable<int>("i32", {}, {}, adios2::Dims{8});
+            auto &var_i64 =
+                io.DefineVariable<long>("i64", {}, {}, adios2::Dims{8});
+            auto &var_u8 =
+                io.DefineVariable<unsigned char>("u8", {}, {}, adios2::Dims{8});
+            auto &var_u16 = io.DefineVariable<unsigned short>("u16", {}, {},
+                                                              adios2::Dims{8});
+            auto &var_u32 =
+                io.DefineVariable<unsigned int>("u32", {}, {}, adios2::Dims{8});
+            auto &var_u64 = io.DefineVariable<unsigned long>("u64", {}, {},
+                                                             adios2::Dims{8});
+            auto &var_r32 =
+                io.DefineVariable<float>("r32", {}, {}, adios2::Dims{8});
+            auto &var_r64 =
+                io.DefineVariable<double>("r64", {}, {}, adios2::Dims{8});
+        }
+
+        // Create the BP Engine
+        io.SetEngine("BPFileWriter");
+        io.SetParameters({{"Threads", "2"}});
+        io.AddTransport("File", {{"Library", "POSIX"}});
+
+        auto engine = io.Open(fname, adios2::OpenMode::Write);
+        ASSERT_NE(engine.get(), nullptr);
+
+        for (size_t step = 0; step < 3; ++step)
+        {
+            // Retrieve the variables that previously went out of scope
+            auto &var_i8 = io.GetVariable<char>("i8");
+            auto &var_i16 = io.GetVariable<short>("i16");
+            auto &var_i32 = io.GetVariable<int>("i32");
+            auto &var_i64 = io.GetVariable<long>("i64");
+            auto &var_u8 = io.GetVariable<unsigned char>("u8");
+            auto &var_u16 = io.GetVariable<unsigned short>("u16");
+            auto &var_u32 = io.GetVariable<unsigned int>("u32");
+            auto &var_u64 = io.GetVariable<unsigned long>("u64");
+            auto &var_r32 = io.GetVariable<float>("r32");
+            auto &var_r64 = io.GetVariable<double>("r64");
+
+            // Write each one
+            engine->Write(var_i8, m_TestData.I8.data() + step);
+            engine->Write(var_i16, m_TestData.I16.data() + step);
+            engine->Write(var_i32, m_TestData.I32.data() + step);
+            engine->Write(var_i64, m_TestData.I64.data() + step);
+            engine->Write(var_u8, m_TestData.U8.data() + step);
+            engine->Write(var_u16, m_TestData.U16.data() + step);
+            engine->Write(var_u32, m_TestData.U32.data() + step);
+            engine->Write(var_u64, m_TestData.U64.data() + step);
+            engine->Write(var_r32, m_TestData.R32.data() + step);
+            engine->Write(var_r64, m_TestData.R64.data() + step);
+
+            // Advance to the next time step
+            engine->Advance();
+        }
+
+        // Close the file
+        engine->Close();
+    }
+
+    // open json file, parse it to a json structure, and verify a few things
+    {
+        std::ifstream profilingJSONFile(fname + ".dir/profiling.json");
+        std::stringstream buffer;
+        buffer << profilingJSONFile.rdbuf();
+
+        const json profilingJSON = json::parse(buffer);
+
+        // check rank is zero
+        const int rank = profilingJSON[0].value("rank", -1);
+        ASSERT_EQ(rank, 0);
+
+        // check threads
+        const int threads = profilingJSON[0].value("threads", 0);
+        ASSERT_EQ(threads, 2);
+
+        // check bytes
+        const unsigned long int bytes = profilingJSON[0].value("bytes", 0UL);
+        ASSERT_EQ(bytes, 6536);
+
+        const auto transportType =
+            profilingJSON[0]["transport_0"].value("type", "0");
+        ASSERT_EQ(transportType, "File_POSIX");
+    }
+}
-- 
GitLab