PEL: peltool: Print all PELs into JSON array

Added -a option to display all PELS at once into a JSON array.

Read message registry once to parse SRC sections in PELs.

Signed-off-by: Harisuddin Mohamed Isa <harisuddin@gmail.com>
Change-Id: I19690a866a3348cf2d8a9a89be38bc09e3eb7f9f
diff --git a/extensions/openpower-pels/pel.cpp b/extensions/openpower-pels/pel.cpp
index 568b4c6..625546e 100644
--- a/extensions/openpower-pels/pel.cpp
+++ b/extensions/openpower-pels/pel.cpp
@@ -210,7 +210,8 @@
 }
 
 void PEL::printSectionInJSON(const Section& section, std::string& buf,
-                             std::map<uint16_t, size_t>& pluralSections) const
+                             std::map<uint16_t, size_t>& pluralSections,
+                             message::Registry& registry) const
 {
     char tmpB[5];
     uint8_t id[] = {static_cast<uint8_t>(section.header().id >> 8),
@@ -231,15 +232,17 @@
 
     if (section.valid())
     {
-        auto json = section.getJSON();
+        auto json = (sectionID == "PS" || sectionID == "SS")
+                        ? section.getJSON(registry)
+                        : section.getJSON();
         if (json)
         {
-            buf += "\n\"" + sectionName + "\": {\n";
+            buf += "\"" + sectionName + "\": {\n";
             buf += *json + "\n},\n";
         }
         else
         {
-            buf += "\n\"" + sectionName + "\": [\n";
+            buf += "\"" + sectionName + "\": [\n";
             std::vector<uint8_t> data;
             Stream s{data};
             section.flatten(s);
@@ -283,16 +286,16 @@
     return sections;
 }
 
-void PEL::toJSON() const
+void PEL::toJSON(message::Registry& registry) const
 {
     auto sections = getPluralSections();
 
-    std::string buf = "{";
-    printSectionInJSON(*(_ph.get()), buf, sections);
-    printSectionInJSON(*(_uh.get()), buf, sections);
+    std::string buf = "{\n";
+    printSectionInJSON(*(_ph.get()), buf, sections, registry);
+    printSectionInJSON(*(_uh.get()), buf, sections, registry);
     for (auto& section : this->optionalSections())
     {
-        printSectionInJSON(*(section.get()), buf, sections);
+        printSectionInJSON(*(section.get()), buf, sections, registry);
     }
     buf += "}";
     std::size_t found = buf.rfind(",");
diff --git a/extensions/openpower-pels/pel.hpp b/extensions/openpower-pels/pel.hpp
index 5f14354..0475c1c 100644
--- a/extensions/openpower-pels/pel.hpp
+++ b/extensions/openpower-pels/pel.hpp
@@ -223,8 +223,9 @@
 
     /**
      * @brief Output a PEL in JSON.
+     * @param[in] registry - Registry object reference
      */
-    void toJSON() const;
+    void toJSON(message::Registry& registry) const;
 
     /**
      * @brief Sets the host transmission state in the User Header
@@ -321,9 +322,11 @@
      * @param[in] std::string - PEL string
      * @param[in|out] pluralSections - Map used to track sections counts for
      *                                 when there is more than 1.
+     * @param[in] registry - Registry object reference
      */
     void printSectionInJSON(const Section& section, std::string& buf,
-                            std::map<uint16_t, size_t>& pluralSections) const;
+                            std::map<uint16_t, size_t>& pluralSections,
+                            message::Registry& registry) const;
 
     /**
      * @brief The maximum size a PEL can be in bytes.
diff --git a/extensions/openpower-pels/section.hpp b/extensions/openpower-pels/section.hpp
index 6353e2d..96a1aad 100644
--- a/extensions/openpower-pels/section.hpp
+++ b/extensions/openpower-pels/section.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "registry.hpp"
 #include "section_header.hpp"
 
 #include <optional>
@@ -51,13 +52,25 @@
     /**
      * @brief Get section in JSON. Derived classes to override when required to.
      * @return std::optional<std::string> - If a section comes with a JSON
-     * repressentation, this would return the string for it.
+     * representation, this would return the string for it.
      */
     virtual std::optional<std::string> getJSON() const
     {
         return std::nullopt;
     }
 
+    /**
+     * @brief Get section in JSON. Derived classes to override when required to.
+     * @param[in] registry - Registry object reference
+     * @return std::optional<std::string> - If a section comes with a JSON
+     * representation, this would return the string for it.
+     */
+    virtual std::optional<std::string>
+        getJSON(message::Registry& registry) const
+    {
+        return std::nullopt;
+    }
+
   protected:
     /**
      * @brief Returns the flattened size of the section header
diff --git a/extensions/openpower-pels/src.cpp b/extensions/openpower-pels/src.cpp
index 4067fe8..6619d3f 100644
--- a/extensions/openpower-pels/src.cpp
+++ b/extensions/openpower-pels/src.cpp
@@ -428,7 +428,7 @@
     return printOut;
 }
 
-std::optional<std::string> SRC::getJSON() const
+std::optional<std::string> SRC::getJSON(message::Registry& registry) const
 {
     std::string ps;
     jsonInsert(ps, "Section Version", getNumberString("%d", _header.version),
@@ -462,8 +462,7 @@
         jsonInsert(ps, "Backplane CCIN", ccinString, 1);
     }
 
-    rg::Registry registry(getMessageRegistryPath() / rg::registryFileName);
-    auto errorDetails = getErrorDetails(registry, DetailLevel::json);
+    auto errorDetails = getErrorDetails(registry, DetailLevel::json, true);
     if (errorDetails)
     {
         ps.append(errorDetails.value());
diff --git a/extensions/openpower-pels/src.hpp b/extensions/openpower-pels/src.hpp
index 7cfa311..51ded24 100644
--- a/extensions/openpower-pels/src.hpp
+++ b/extensions/openpower-pels/src.hpp
@@ -225,9 +225,11 @@
 
     /**
      * @brief Get section in JSON.
+     * @param[in] registry - Registry object reference
      * @return std::optional<std::string> - SRC section's JSON
      */
-    std::optional<std::string> getJSON() const override;
+    std::optional<std::string>
+        getJSON(message::Registry& registry) const override;
 
     /**
      * @brief Get error details based on refcode and hexwords
diff --git a/extensions/openpower-pels/tools/peltool.cpp b/extensions/openpower-pels/tools/peltool.cpp
index fb4153e..142a712 100644
--- a/extensions/openpower-pels/tools/peltool.cpp
+++ b/extensions/openpower-pels/tools/peltool.cpp
@@ -39,7 +39,8 @@
 namespace pv = openpower::pels::pel_values;
 
 using PELFunc = std::function<void(const PEL&)>;
-
+message::Registry registry(getMessageRegistryPath() /
+                           message::registryFileName);
 namespace service
 {
 constexpr auto logging = "xyz.openbmc_project.Logging";
@@ -202,8 +203,17 @@
     }
 }
 
+/**
+ * @brief Creates JSON string of a PEL entry if fullPEL is false or prints to
+ *        stdout the full PEL in JSON if fullPEL is true
+ * @param[in] itr - std::map iterator of <uint32_t, BCDTime>
+ * @param[in] hidden - Boolean to include hidden PELs
+ * @param[in] fullPEL - Boolean to print full JSON representation of PEL
+ * @param[in] foundPEL - Boolean to check if any PEL is present
+ * @return std::string - JSON string of PEL entry (empty if fullPEL is true)
+ */
 template <typename T>
-std::string genPELJSON(T itr, bool hidden, message::Registry& registry)
+std::string genPELJSON(T itr, bool hidden, bool fullPEL, bool& foundPEL)
 {
     std::size_t found;
     std::string val;
@@ -219,11 +229,30 @@
     try
     {
         std::vector<uint8_t> data = getFileData(fileName);
-        if (!data.empty())
+        if (data.empty())
         {
-            PEL pel{data};
-            std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
-            if (pel.valid() && (hidden || !actionFlags.test(hiddenFlagBit)))
+            log<level::ERR>("Empty PEL file",
+                            entry("FILENAME=%s", fileName.c_str()));
+            return listStr;
+        }
+        PEL pel{data};
+        std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
+        if (pel.valid() && (hidden || !actionFlags.test(hiddenFlagBit)))
+        {
+            if (fullPEL)
+            {
+                if (!foundPEL)
+                {
+                    std::cout << "[" << std::endl;
+                    foundPEL = true;
+                }
+                else
+                {
+                    std::cout << ",\n" << std::endl;
+                }
+                pel.toJSON(registry);
+            }
+            else
             {
                 // id
                 sprintf(tmpValStr, "0x%X", pel.privateHeader().id());
@@ -292,12 +321,6 @@
                 }
             }
         }
-        else
-        {
-            log<level::ERR>("Empty PEL file",
-                            entry("FILENAME=%s", fileName.c_str()),
-                            entry("ERROR=%s", "Empty PEL file"));
-        }
     }
     catch (std::exception& e)
     {
@@ -307,14 +330,17 @@
     }
     return listStr;
 }
+
 /**
- * @brief Print a list of PELs
+ * @brief Print a list of PELs or a JSON array of PELs
+ * @param[in] order - Boolean to print in reverse orser
+ * @param[in] hidden - Boolean to include hidden PELs
+ * @param[in] fullPEL - Boolean to print full PEL into a JSON array
  */
-void printList(bool order, bool hidden)
+void printPELs(bool order, bool hidden, bool fullPEL)
 {
     std::string listStr;
     std::map<uint32_t, BCDTime> PELs;
-    std::size_t found;
     listStr = "{\n";
     for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
          it != fs::directory_iterator(); ++it)
@@ -329,10 +355,9 @@
                          fileNameToTimestamp((*it).path().filename()));
         }
     }
-    message::Registry registry(getMessageRegistryPath() /
-                               message::registryFileName);
-    auto buildJSON = [&listStr, &hidden, &registry](const auto& i) {
-        listStr += genPELJSON(i, hidden, registry);
+    bool foundPEL = false;
+    auto buildJSON = [&listStr, &hidden, &fullPEL, &foundPEL](const auto& i) {
+        listStr += genPELJSON(i, hidden, fullPEL, foundPEL);
     };
     if (order)
     {
@@ -343,12 +368,20 @@
         std::for_each(PELs.begin(), PELs.end(), buildJSON);
     }
 
-    found = listStr.rfind(",");
-    if (found != std::string::npos)
+    if (!fullPEL)
     {
-        listStr.replace(found, 1, "");
-        listStr += "\n}\n";
-        printf("%s", listStr.c_str());
+        std::size_t found;
+        found = listStr.rfind(",");
+        if (found != std::string::npos)
+        {
+            listStr.replace(found, 1, "");
+            listStr += "\n}\n";
+            printf("%s", listStr.c_str());
+        }
+    }
+    else if (foundPEL)
+    {
+        std::cout << "]" << std::endl;
     }
 }
 
@@ -477,7 +510,7 @@
 {
     if (pel.valid())
     {
-        pel.toJSON();
+        pel.toJSON(registry);
     }
     else
     {
@@ -536,11 +569,13 @@
     bool hidden = false;
     bool deleteAll = false;
     bool showPELCount = false;
+    bool fullPEL = false;
 
     app.set_help_flag("--help", "Print this help message and exit");
     app.add_option("-f,--file", fileName,
                    "Display a PEL using its Raw PEL file");
     app.add_option("-i, --id", idPEL, "Display a PEL based on its ID");
+    app.add_flag("-a", fullPEL, "Display all PELs");
     app.add_flag("-l", listPEL, "List PELs");
     app.add_flag("-n", showPELCount, "Show number of PELs");
     app.add_flag("-r", listPELDescOrd, "Reverse order of output");
@@ -556,7 +591,7 @@
         if (!data.empty())
         {
             PEL pel{data};
-            pel.toJSON();
+            pel.toJSON(registry);
         }
         else
         {
@@ -564,14 +599,13 @@
                           "Raw PEL file can't be read.");
         }
     }
-
     else if (!idPEL.empty())
     {
         callFunctionOnPEL(idPEL, displayPEL);
     }
-    else if (listPEL)
+    else if (fullPEL || listPEL)
     {
-        printList(listPELDescOrd, hidden);
+        printPELs(listPELDescOrd, hidden, fullPEL);
     }
     else if (showPELCount)
     {
@@ -587,8 +621,7 @@
     }
     else
     {
-        exitWithError(app.help("", CLI::AppFormatMode::All),
-                      "Raw PEL file path not specified.");
+        std::cout << app.help("", CLI::AppFormatMode::All) << std::endl;
     }
     return 0;
 }