peltool: Add option to print PEL hexdump

This adds the "-x" option to simply hexdump PELs instead of parsing and
printing to JSON, for use with the --file, -i or -a options.

Signed-off-by: Harisuddin Mohamed Isa <harisuddin@gmail.com>
Change-Id: I13cc76ea36cca761ffa1cf3ab38a638e424bde83
diff --git a/extensions/openpower-pels/json_utils.cpp b/extensions/openpower-pels/json_utils.cpp
index d45b542..e6007ed 100644
--- a/extensions/openpower-pels/json_utils.cpp
+++ b/extensions/openpower-pels/json_utils.cpp
@@ -67,13 +67,17 @@
 
     return output;
 }
-char* dumpHex(const void* data, size_t size, size_t indentCount)
+char* dumpHex(const void* data, size_t size, size_t indentCount, bool toJson)
 {
     const int symbolSize = 100;
     std::string jsonIndent(indentLevel * indentCount, 0x20);
-    jsonIndent.append("\"");
+    if (toJson)
+    {
+        jsonIndent.append("\"");
+    }
     char* buffer = (char*)calloc(std::max(70, 10 * (int)size), sizeof(char));
     char* symbol = (char*)calloc(symbolSize, sizeof(char));
+    char* byteCount = (char*)calloc(11, sizeof(char));
     char ascii[17];
     size_t i, j;
     ascii[16] = '\0';
@@ -81,6 +85,11 @@
     {
         if (i % 16 == 0)
         {
+            if (!toJson)
+            {
+                snprintf(byteCount, 11, "%08X  ", static_cast<uint32_t>(i));
+                strcat(buffer, byteCount);
+            }
             strcat(buffer, jsonIndent.c_str());
         }
         snprintf(symbol, symbolSize, "%02X ", ((unsigned char*)data)[i]);
@@ -98,18 +107,27 @@
         if ((i + 1) % 8 == 0 || i + 1 == size)
         {
             std::string asciiString(ascii);
-            asciiString = escapeJSON(asciiString);
-            const char* asciiToPrint = asciiString.c_str();
+            if (toJson)
+            {
+                asciiString = escapeJSON(asciiString);
+            }
             strcat(buffer, " ");
             if ((i + 1) % 16 == 0)
             {
-                if (i + 1 != size)
+                if (i + 1 != size && toJson)
                 {
-                    snprintf(symbol, symbolSize, "|  %s\",\n", asciiToPrint);
+                    snprintf(symbol, symbolSize, "|  %s\",\n",
+                             asciiString.c_str());
+                }
+                else if (toJson)
+                {
+                    snprintf(symbol, symbolSize, "|  %s\"\n",
+                             asciiString.c_str());
                 }
                 else
                 {
-                    snprintf(symbol, symbolSize, "|  %s\"\n", asciiToPrint);
+                    snprintf(symbol, symbolSize, "|  %s\n",
+                             asciiString.c_str());
                 }
                 strcat(buffer, symbol);
                 memset(symbol, 0, strlen(symbol));
@@ -126,14 +144,24 @@
                     strcat(buffer, "   ");
                 }
                 std::string asciiString2(ascii);
-                asciiString2 = escapeJSON(asciiString2);
-                asciiToPrint = asciiString2.c_str();
-                snprintf(symbol, symbolSize, "|  %s\"\n", asciiToPrint);
+                if (toJson)
+                {
+                    asciiString2 = escapeJSON(asciiString2);
+                    snprintf(symbol, symbolSize, "|  %s\"\n",
+                             asciiString2.c_str());
+                }
+                else
+                {
+                    snprintf(symbol, symbolSize, "|  %s\n",
+                             asciiString2.c_str());
+                }
+
                 strcat(buffer, symbol);
                 memset(symbol, 0, strlen(symbol));
             }
         }
     }
+    free(byteCount);
     free(symbol);
     return buffer;
 }
diff --git a/extensions/openpower-pels/json_utils.hpp b/extensions/openpower-pels/json_utils.hpp
index 71a85e3..f6db297 100644
--- a/extensions/openpower-pels/json_utils.hpp
+++ b/extensions/openpower-pels/json_utils.hpp
@@ -27,9 +27,12 @@
  * @param[in] const void* data - Raw PEL data
  * @param[i] size_t size - size of Raw PEL
  * @param[in] size_t indentCount - The number of indent levels to indent
+ * @param[in] bool toJson - if true, output lines as JSON array, else print
+ *            output as plain text
  * @return char * - the Hex dump
  */
-char* dumpHex(const void* data, size_t size, size_t indentCount);
+char* dumpHex(const void* data, size_t size, size_t indentCount,
+              bool toJson = true);
 
 /**
  * @brief Inserts key-value into a JSON string
diff --git a/extensions/openpower-pels/tools/peltool.cpp b/extensions/openpower-pels/tools/peltool.cpp
index 772cb84..9926833 100644
--- a/extensions/openpower-pels/tools/peltool.cpp
+++ b/extensions/openpower-pels/tools/peltool.cpp
@@ -41,7 +41,7 @@
 namespace message = openpower::pels::message;
 namespace pv = openpower::pels::pel_values;
 
-using PELFunc = std::function<void(const PEL&)>;
+using PELFunc = std::function<void(const PEL&, bool hexDump)>;
 message::Registry registry(getPELReadOnlyDataPath() / message::registryFileName,
                            false);
 namespace service
@@ -271,13 +271,14 @@
  * @param[in] foundPEL - Boolean to check if any PEL is present
  * @param[in] scrubRegex - SRC regex object
  * @param[in] plugins - Vector of strings of plugins found in filesystem
+ * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
  * @return std::string - JSON string of PEL entry (empty if fullPEL is true)
  */
 template <typename T>
 std::string genPELJSON(T itr, bool hidden, bool includeInfo, bool fullPEL,
                        bool& foundPEL,
                        const std::optional<std::regex>& scrubRegex,
-                       const std::vector<std::string>& plugins)
+                       const std::vector<std::string>& plugins, bool hexDump)
 {
     std::size_t found;
     std::string val;
@@ -322,7 +323,12 @@
                 return listStr;
             }
         }
-        if (fullPEL)
+        if (hexDump)
+        {
+            std::cout << dumpHex(std::data(pel.data()), pel.size(), 0, false)
+                      << std::endl;
+        }
+        else if (fullPEL)
         {
             if (!foundPEL)
             {
@@ -418,9 +424,10 @@
  * @param[in] includeInfo - Boolean to include informational PELs
  * @param[in] fullPEL - Boolean to print full PEL into a JSON array
  * @param[in] scrubRegex - SRC regex object
+ * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
  */
 void printPELs(bool order, bool hidden, bool includeInfo, bool fullPEL,
-               const std::optional<std::regex>& scrubRegex)
+               const std::optional<std::regex>& scrubRegex, bool hexDump)
 {
     std::string listStr;
     std::map<uint32_t, BCDTime> PELs;
@@ -440,14 +447,15 @@
         }
     }
     bool foundPEL = false;
-    if (fullPEL)
+
+    if (fullPEL && !hexDump)
     {
         plugins = getPlugins();
     }
     auto buildJSON = [&listStr, &hidden, &includeInfo, &fullPEL, &foundPEL,
-                      &scrubRegex, &plugins](const auto& i) {
+                      &scrubRegex, &plugins, &hexDump](const auto& i) {
         listStr += genPELJSON(i, hidden, includeInfo, fullPEL, foundPEL,
-                              scrubRegex, plugins);
+                              scrubRegex, plugins, hexDump);
     };
     if (order)
     {
@@ -457,7 +465,10 @@
     {
         std::for_each(PELs.begin(), PELs.end(), buildJSON);
     }
-
+    if (hexDump)
+    {
+        return;
+    }
     if (foundPEL)
     {
         if (fullPEL)
@@ -489,10 +500,13 @@
  *
  * @param[in] id - The string version of the PEL or BMC Log ID, either with or
  *                 without the 0x prefix.
- * @param[in] func - The std::function<void(const PEL&)> function to run.
+ * @param[in] func - The std::function<void(const PEL&, bool hexDump)> function
+ *                   to run.
  * @param[in] useBMC - if true, search by BMC Log ID, else search by PEL ID
+ * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
  */
-void callFunctionOnPEL(const std::string& id, const PELFunc& func, bool useBMC)
+void callFunctionOnPEL(const std::string& id, const PELFunc& func,
+                       bool useBMC = false, bool hexDump = false)
 {
     std::string pelID{id};
     if (!useBMC)
@@ -530,7 +544,7 @@
                     found = true;
                     try
                     {
-                        func(pel);
+                        func(pel, hexDump);
                         break;
                     }
                     catch (std::exception& e)
@@ -560,8 +574,9 @@
  * @brief Delete a PEL by deleting its corresponding event log.
  *
  * @param[in] pel - The PEL to delete
+ * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON (unused)
  */
-void deletePEL(const PEL& pel)
+void deletePEL(const PEL& pel, bool hexDump = false)
 {
     std::string path{object_path::logEntry};
     path += std::to_string(pel.obmcLogID());
@@ -609,13 +624,23 @@
  * @brief Display a single PEL
  *
  * @param[in] pel - the PEL to display
+ * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
  */
-void displayPEL(const PEL& pel)
+void displayPEL(const PEL& pel, bool hexDump)
 {
     if (pel.valid())
     {
-        auto plugins = getPlugins();
-        pel.toJSON(registry, plugins);
+        if (hexDump)
+        {
+            std::string dstr =
+                dumpHex(std::data(pel.data()), pel.size(), 0, false);
+            std::cout << dstr << std::endl;
+        }
+        else
+        {
+            auto plugins = getPlugins();
+            pel.toJSON(registry, plugins);
+        }
     }
     else
     {
@@ -774,6 +799,7 @@
     bool deleteAll = false;
     bool showPELCount = false;
     bool fullPEL = false;
+    bool hexDump = false;
 
     app.set_help_flag("--help", "Print this help message and exit");
     app.add_option("--file", fileName, "Display a PEL using its Raw PEL file");
@@ -790,6 +816,7 @@
     app.add_flag("-D, --delete-all", deleteAll, "Delete all PELs");
     app.add_option("-s, --scrub", scrubFile,
                    "File containing SRC regular expressions to ignore");
+    app.add_flag("-x", hexDump, "Display PEL(s) in hexdump instead of JSON");
 
     CLI11_PARSE(app, argc, argv);
 
@@ -798,9 +825,18 @@
         std::vector<uint8_t> data = getFileData(fileName);
         if (!data.empty())
         {
-            auto plugins = getPlugins();
             PEL pel{data};
-            pel.toJSON(registry, plugins);
+            if (hexDump)
+            {
+                std::string dstr =
+                    dumpHex(std::data(pel.data()), pel.size(), 0, false);
+                std::cout << dstr << std::endl;
+            }
+            else
+            {
+                auto plugins = getPlugins();
+                pel.toJSON(registry, plugins);
+            }
         }
         else
         {
@@ -810,11 +846,11 @@
     }
     else if (!idPEL.empty())
     {
-        callFunctionOnPEL(idPEL, displayPEL, false);
+        callFunctionOnPEL(idPEL, displayPEL, false, hexDump);
     }
     else if (!bmcId.empty())
     {
-        callFunctionOnPEL(bmcId, displayPEL, true);
+        callFunctionOnPEL(bmcId, displayPEL, true, hexDump);
     }
     else if (fullPEL || listPEL)
     {
@@ -822,7 +858,8 @@
         {
             scrubRegex = genRegex(scrubFile);
         }
-        printPELs(listPELDescOrd, hidden, includeInfo, fullPEL, scrubRegex);
+        printPELs(listPELDescOrd, hidden, includeInfo, fullPEL, scrubRegex,
+                  hexDump);
     }
     else if (showPELCount)
     {
@@ -834,7 +871,7 @@
     }
     else if (!idToDelete.empty())
     {
-        callFunctionOnPEL(idToDelete, deletePEL, false);
+        callFunctionOnPEL(idToDelete, deletePEL);
     }
     else if (deleteAll)
     {