PEL: Print component names in peltool

Every PEL section has a 2 byte component ID field in its header that
peltool prints.  Currently, it just prints the hex version, like
"0x1000".

There are JSON files in the flash already that contain mappings of
component IDs to to component names, and this commit starts looking
up the component names from those files and using those in the peltool
output.

An example of a file is:
/usr/share/phosphor-logging/pels/O_component_ids.json:
{
    "1000": "bmc common function",
    "2000": "bmc error logging",
    ...
}

Where the 'O' in the filename is the creator ID field of the PEL.  There
is also a file for hostboot, which is B_component_ids.json.

Also, for PELs with a PHYP creator ID, just convert the ID to two
characters, like 0x4552 - > "ER" as that is what they are.

peltool output examples:
    "Created by": "bmc error logging",
    "Created by": "hostboot: errl",
    "Created by": "IO",

This matches what is already done by the python peltool.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: Id616739e1b7ca67c85dc7efa85dc34acf6aca9b5
diff --git a/extensions/openpower-pels/extended_user_header.cpp b/extensions/openpower-pels/extended_user_header.cpp
index 5be0a86..4f54518 100644
--- a/extensions/openpower-pels/extended_user_header.cpp
+++ b/extensions/openpower-pels/extended_user_header.cpp
@@ -185,13 +185,13 @@
     _symptomIDSize = _symptomID.size();
 }
 
-std::optional<std::string> ExtendedUserHeader::getJSON() const
+std::optional<std::string> ExtendedUserHeader::getJSON(uint8_t creatorID) const
 {
     std::string json;
     jsonInsert(json, pv::sectionVer, getNumberString("%d", _header.version), 1);
     jsonInsert(json, pv::subSection, getNumberString("%d", _header.subType), 1);
     jsonInsert(json, pv::createdBy,
-               getNumberString("0x%X", _header.componentID), 1);
+               getComponentName(_header.componentID, creatorID), 1);
     jsonInsert(json, "Reporting Machine Type", machineTypeModel(), 1);
     jsonInsert(json, "Reporting Serial Number", trimEnd(machineSerialNumber()),
                1);
diff --git a/extensions/openpower-pels/extended_user_header.hpp b/extensions/openpower-pels/extended_user_header.hpp
index b5a447d..40da0b6 100644
--- a/extensions/openpower-pels/extended_user_header.hpp
+++ b/extensions/openpower-pels/extended_user_header.hpp
@@ -157,9 +157,12 @@
 
     /**
      * @brief Get section in JSON.
+     *
+     * @param[in] creatorID - The creator ID for the PEL
+     *
      * @return std::optional<std::string> - ExtendedUserHeader section's JSON
      */
-    std::optional<std::string> getJSON() const override;
+    std::optional<std::string> getJSON(uint8_t creatorID) const override;
 
   private:
     /**
diff --git a/extensions/openpower-pels/failing_mtms.cpp b/extensions/openpower-pels/failing_mtms.cpp
index d4db58f..a8b33d3 100644
--- a/extensions/openpower-pels/failing_mtms.cpp
+++ b/extensions/openpower-pels/failing_mtms.cpp
@@ -93,13 +93,13 @@
     stream >> _header >> _mtms;
 }
 
-std::optional<std::string> FailingMTMS::getJSON() const
+std::optional<std::string> FailingMTMS::getJSON(uint8_t creatorID) const
 {
     std::string json;
     jsonInsert(json, pv::sectionVer, getNumberString("%d", _header.version), 1);
     jsonInsert(json, pv::subSection, getNumberString("%d", _header.subType), 1);
     jsonInsert(json, pv::createdBy,
-               getNumberString("0x%X", _header.componentID), 1);
+               getComponentName(_header.componentID, creatorID), 1);
     jsonInsert(json, "Machine Type Model", _mtms.machineTypeAndModel(), 1);
     jsonInsert(json, "Serial Number", trimEnd(_mtms.machineSerialNumber()), 1);
     json.erase(json.size() - 2);
diff --git a/extensions/openpower-pels/failing_mtms.hpp b/extensions/openpower-pels/failing_mtms.hpp
index 8f5cd44..b14f1ef 100644
--- a/extensions/openpower-pels/failing_mtms.hpp
+++ b/extensions/openpower-pels/failing_mtms.hpp
@@ -83,9 +83,12 @@
 
     /**
      * @brief Get section in JSON.
+     *
+     * @param[in] creatorID - The creator ID for the PEL
+     *
      * @return std::optional<std::string> - Failing MTMS section in JSON
      */
-    std::optional<std::string> getJSON() const override;
+    std::optional<std::string> getJSON(uint8_t creatorID) const override;
 
   private:
     /**
diff --git a/extensions/openpower-pels/json_utils.cpp b/extensions/openpower-pels/json_utils.cpp
index 08c816c..13094ed 100644
--- a/extensions/openpower-pels/json_utils.cpp
+++ b/extensions/openpower-pels/json_utils.cpp
@@ -15,9 +15,15 @@
  */
 #include "json_utils.hpp"
 
+#include "paths.hpp"
+
 #include <stdio.h>
 
+#include <nlohmann/json.hpp>
+
 #include <cstring>
+#include <filesystem>
+#include <optional>
 #include <sstream>
 #include <string>
 
@@ -232,5 +238,109 @@
     }
     return s;
 }
+
+/**
+ * @brief Lookup the component ID in a JSON file named
+ *        after the creator ID.
+ *
+ * Keeps a cache of the JSON it reads to live throughout
+ * the peltool call as the JSON can be reused across
+ * PEL sections or even across PELs.
+ *
+ * @param[in] compID - The component ID
+ * @param[in] creatorID - The creator ID for the PEL
+ * @return optional<string> - The comp name, or std::nullopt
+ */
+static std::optional<std::string> lookupComponentName(uint16_t compID,
+                                                      char creatorID)
+{
+    static std::map<char, nlohmann::json> jsonCache;
+    nlohmann::json jsonData;
+    nlohmann::json* jsonPtr = &jsonData;
+    std::filesystem::path filename{std::string{creatorID} +
+                                   "_component_ids.json"};
+    filename = getPELReadOnlyDataPath() / filename;
+
+    auto jsonIt = jsonCache.find(creatorID);
+    if (jsonIt != jsonCache.end())
+    {
+        jsonPtr = &(jsonIt->second);
+    }
+    else
+    {
+        std::error_code ec;
+        if (!std::filesystem::exists(filename, ec))
+        {
+            return std::nullopt;
+        }
+
+        std::ifstream file{filename};
+        if (!file)
+        {
+            return std::nullopt;
+        }
+
+        jsonData = nlohmann::json::parse(file, nullptr, false);
+        if (jsonData.is_discarded())
+        {
+            return std::nullopt;
+        }
+
+        jsonCache.emplace(creatorID, jsonData);
+    }
+
+    auto id = getNumberString("%04X", compID);
+
+    auto it = jsonPtr->find(id);
+    if (it == jsonPtr->end())
+    {
+        return std::nullopt;
+    }
+
+    return it->get<std::string>();
+}
+
+/**
+ * @brief Convert the component ID to a 2 character string
+ *        if both bytes are nonzero
+ *
+ * e.g. 0x4552 -> "ER"
+ *
+ * @param[in] compID - The component ID
+ * @return optional<string> - The two character string, or std::nullopt.
+ */
+static std::optional<std::string> convertCompIDToChars(uint16_t compID)
+{
+    uint8_t first = (compID >> 8) & 0xFF;
+    uint8_t second = compID & 0xFF;
+    if ((first != 0) && (second != 0))
+    {
+        std::string id{static_cast<char>(first)};
+        id += static_cast<char>(second);
+        return id;
+    }
+
+    return std::nullopt;
+}
+
+std::string getComponentName(uint16_t compID, uint8_t creatorID)
+{
+    // See if there's a JSON file with the names
+    auto name = lookupComponentName(compID, creatorID);
+
+    // If PHYP, convert to ASCII
+    if (!name && ('H' == creatorID))
+    {
+        name = convertCompIDToChars(compID);
+    }
+
+    if (!name)
+    {
+        name = getNumberString("0x%04X", compID);
+    }
+
+    return *name;
+}
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/json_utils.hpp b/extensions/openpower-pels/json_utils.hpp
index 02b8a83..4ee32f4 100644
--- a/extensions/openpower-pels/json_utils.hpp
+++ b/extensions/openpower-pels/json_utils.hpp
@@ -93,5 +93,19 @@
  */
 std::string trimEnd(std::string s);
 
+/**
+ * @brief Returns the component name for the component ID.
+ *
+ * It will try to look up the name to use in JSON files based on
+ * the creator ID.  If PHYP, will convert the component ID to
+ * two characters.
+ *
+ * If nothing else, it will just return the name as a string like
+ * "0x1234".
+ *
+ * @return std::string - The component name
+ */
+std::string getComponentName(uint16_t compID, uint8_t creatorID);
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/pel.cpp b/extensions/openpower-pels/pel.cpp
index 9fa1747..3b7ecf5 100644
--- a/extensions/openpower-pels/pel.cpp
+++ b/extensions/openpower-pels/pel.cpp
@@ -373,7 +373,7 @@
         }
         else
         {
-            json = section.getJSON();
+            json = section.getJSON(creatorID);
         }
 
         buf += "\"" + sectionName + "\": {\n";
@@ -447,8 +447,10 @@
     auto sections = getPluralSections();
 
     std::string buf = "{\n";
-    printSectionInJSON(*(_ph.get()), buf, sections, registry, plugins);
-    printSectionInJSON(*(_uh.get()), buf, sections, registry, plugins);
+    printSectionInJSON(*(_ph.get()), buf, sections, registry, plugins,
+                       _ph->creatorID());
+    printSectionInJSON(*(_uh.get()), buf, sections, registry, plugins,
+                       _ph->creatorID());
     for (auto& section : this->optionalSections())
     {
         printSectionInJSON(*(section.get()), buf, sections, registry, plugins,
diff --git a/extensions/openpower-pels/private_header.cpp b/extensions/openpower-pels/private_header.cpp
index 00c09d0..ff49535 100644
--- a/extensions/openpower-pels/private_header.cpp
+++ b/extensions/openpower-pels/private_header.cpp
@@ -85,7 +85,7 @@
         _valid = false;
     }
 }
-std::optional<std::string> PrivateHeader::getJSON() const
+std::optional<std::string> PrivateHeader::getJSON(uint8_t creatorID) const
 {
     char tmpPhVal[50];
     sprintf(tmpPhVal, "%02X/%02X/%02X%02X %02X:%02X:%02X",
@@ -110,8 +110,8 @@
     jsonInsert(ph, pv::sectionVer, getNumberString("%d", privateHeaderVersion),
                1);
     jsonInsert(ph, pv::subSection, getNumberString("%d", _header.subType), 1);
-    jsonInsert(ph, pv::createdBy, getNumberString("0x%X", _header.componentID),
-               1);
+    jsonInsert(ph, pv::createdBy,
+               getComponentName(_header.componentID, creatorID), 1);
     jsonInsert(ph, "Created at", phCreateTStr, 1);
     jsonInsert(ph, "Committed at", phCommitTStr, 1);
     jsonInsert(ph, "Creator Subsystem", creator, 1);
diff --git a/extensions/openpower-pels/private_header.hpp b/extensions/openpower-pels/private_header.hpp
index dd0d504..224142c 100644
--- a/extensions/openpower-pels/private_header.hpp
+++ b/extensions/openpower-pels/private_header.hpp
@@ -221,9 +221,12 @@
 
     /**
      * @brief Get section in JSON.
+     *
+     * @param[in] creatorID - The creator ID for the PEL
+     *
      * @return std::optional<std::string> - Private header section's JSON
      */
-    std::optional<std::string> getJSON() const override;
+    std::optional<std::string> getJSON(uint8_t creatorID) const override;
 
   private:
     /**
diff --git a/extensions/openpower-pels/section.hpp b/extensions/openpower-pels/section.hpp
index 64bcda3..08f164c 100644
--- a/extensions/openpower-pels/section.hpp
+++ b/extensions/openpower-pels/section.hpp
@@ -51,10 +51,13 @@
 
     /**
      * @brief Get section in JSON. Derived classes to override when required to.
+     *
+     * @param[in] creatorID - The creator ID for the PEL
+     *
      * @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() const
+    virtual std::optional<std::string> getJSON(uint8_t /* creatorID*/) const
     {
         return std::nullopt;
     }
diff --git a/extensions/openpower-pels/src.cpp b/extensions/openpower-pels/src.cpp
index e195fa7..063c2ad 100644
--- a/extensions/openpower-pels/src.cpp
+++ b/extensions/openpower-pels/src.cpp
@@ -707,8 +707,8 @@
     std::vector<std::string> hexwords;
     jsonInsert(ps, pv::sectionVer, getNumberString("%d", _header.version), 1);
     jsonInsert(ps, pv::subSection, getNumberString("%d", _header.subType), 1);
-    jsonInsert(ps, pv::createdBy, getNumberString("0x%X", _header.componentID),
-               1);
+    jsonInsert(ps, pv::createdBy,
+               getComponentName(_header.componentID, creatorID), 1);
     jsonInsert(ps, "SRC Version", getNumberString("0x%02X", _version), 1);
     jsonInsert(ps, "SRC Format", getNumberString("0x%02X", _hexData[0] & 0xFF),
                1);
diff --git a/extensions/openpower-pels/user_data_json.cpp b/extensions/openpower-pels/user_data_json.cpp
index bf6152b..0e3ac24 100644
--- a/extensions/openpower-pels/user_data_json.cpp
+++ b/extensions/openpower-pels/user_data_json.cpp
@@ -48,17 +48,19 @@
  * the outer {}.  If the input JSON isn't a JSON object (dict), then
  * one will be created with the input added to a 'Data' key.
  *
+ * @param[in] creatorID - The creator ID for the PEL
+ *
  * @param[in] json - The JSON to convert to a string
  *
  * @return std::string - The JSON string
  */
 std::string prettyJSON(uint16_t componentID, uint8_t subType, uint8_t version,
-                       const orderedJSON& json)
+                       uint8_t creatorID, const orderedJSON& json)
 {
     orderedJSON output;
     output[pv::sectionVer] = std::to_string(version);
     output[pv::subSection] = std::to_string(subType);
-    output[pv::createdBy] = getNumberString("0x%04X", componentID);
+    output[pv::createdBy] = getComponentName(componentID, creatorID);
 
     if (!json.is_object())
     {
@@ -103,12 +105,13 @@
  * @param[in] componentID - The comp ID from the UserData section header
  * @param[in] subType - The subtype from the UserData section header
  * @param[in] version - The version from the UserData section header
+ * @param[in] creatorID - The creator ID for the PEL
  * @param[in] data - The CBOR data
  *
  * @return std::string - The JSON string
  */
 std::string getCBORJSON(uint16_t componentID, uint8_t subType, uint8_t version,
-                        const std::vector<uint8_t>& data)
+                        uint8_t creatorID, const std::vector<uint8_t>& data)
 {
     // The CBOR parser needs the pad bytes added to 4 byte align
     // removed.  The number of bytes added to the pad is on the
@@ -131,7 +134,7 @@
 
     orderedJSON json = orderedJSON::from_cbor(cborData);
 
-    return prettyJSON(componentID, subType, version, json);
+    return prettyJSON(componentID, subType, version, creatorID, json);
 }
 
 /**
@@ -144,12 +147,13 @@
  * @param[in] componentID - The comp ID from the UserData section header
  * @param[in] subType - The subtype from the UserData section header
  * @param[in] version - The version from the UserData section header
+ * @param[in] creatorID - The creator ID for the PEL
  * @param[in] data - The CBOR data
  *
  * @return std::string - The JSON string
  */
 std::string getTextJSON(uint16_t componentID, uint8_t subType, uint8_t version,
-                        const std::vector<uint8_t>& data)
+                        uint8_t creatorID, const std::vector<uint8_t>& data)
 {
     std::vector<std::string> text;
     size_t startPos = 0;
@@ -183,7 +187,7 @@
     }
 
     orderedJSON json = text;
-    return prettyJSON(componentID, subType, version, json);
+    return prettyJSON(componentID, subType, version, creatorID, json);
 }
 
 /**
@@ -200,7 +204,7 @@
  */
 std::optional<std::string>
     getBuiltinFormatJSON(uint16_t componentID, uint8_t subType, uint8_t version,
-                         const std::vector<uint8_t>& data)
+                         const std::vector<uint8_t>& data, uint8_t creatorID)
 {
     switch (subType)
     {
@@ -210,15 +214,15 @@
 
             orderedJSON json = orderedJSON::parse(jsonString);
 
-            return prettyJSON(componentID, subType, version, json);
+            return prettyJSON(componentID, subType, version, creatorID, json);
         }
         case static_cast<uint8_t>(UserDataFormat::cbor):
         {
-            return getCBORJSON(componentID, subType, version, data);
+            return getCBORJSON(componentID, subType, version, creatorID, data);
         }
         case static_cast<uint8_t>(UserDataFormat::text):
         {
-            return getTextJSON(componentID, subType, version, data);
+            return getTextJSON(componentID, subType, version, creatorID, data);
         }
         default:
             break;
@@ -342,7 +346,8 @@
                         (json.is_array() && json.size() > 0) ||
                         (json.is_string() && json != ""))
                     {
-                        return prettyJSON(componentID, subType, version, json);
+                        return prettyJSON(componentID, subType, version,
+                                          creatorID, json);
                     }
                 }
                 catch (const std::exception& e)
@@ -406,7 +411,8 @@
         if (pv::creatorIDs.at(getNumberString("%c", creatorID)) == "BMC" &&
             componentID == static_cast<uint16_t>(ComponentID::phosphorLogging))
         {
-            return getBuiltinFormatJSON(componentID, subType, version, data);
+            return getBuiltinFormatJSON(componentID, subType, version, data,
+                                        creatorID);
         }
         else if (std::find(plugins.begin(), plugins.end(),
                            subsystem + component) != plugins.end())
diff --git a/extensions/openpower-pels/user_header.cpp b/extensions/openpower-pels/user_header.cpp
index 813457f..c7fff1a 100644
--- a/extensions/openpower-pels/user_header.cpp
+++ b/extensions/openpower-pels/user_header.cpp
@@ -257,7 +257,7 @@
     _valid = (failed) ? false : true;
 }
 
-std::optional<std::string> UserHeader::getJSON() const
+std::optional<std::string> UserHeader::getJSON(uint8_t creatorID) const
 {
     std::string severity;
     std::string subsystem;
@@ -290,7 +290,7 @@
     jsonInsert(uh, pv::sectionVer, getNumberString("%d", userHeaderVersion), 1);
     jsonInsert(uh, pv::subSection, getNumberString("%d", _header.subType), 1);
     jsonInsert(uh, "Log Committed by",
-               getNumberString("0x%X", _header.componentID), 1);
+               getComponentName(_header.componentID, creatorID), 1);
     jsonInsert(uh, "Subsystem", subsystem, 1);
     jsonInsert(uh, "Event Scope", eventScope, 1);
     jsonInsert(uh, "Event Severity", severity, 1);
diff --git a/extensions/openpower-pels/user_header.hpp b/extensions/openpower-pels/user_header.hpp
index e17d0cf..5bfe6d3 100644
--- a/extensions/openpower-pels/user_header.hpp
+++ b/extensions/openpower-pels/user_header.hpp
@@ -220,9 +220,12 @@
 
     /**
      * @brief Get section in JSON.
+     *
+     * @param[in] creatorID - The creator ID for the PEL
+     *
      * @return std::optional<std::string> -User header section's JSON
      */
-    std::optional<std::string> getJSON() const override;
+    std::optional<std::string> getJSON(uint8_t creatorID) const override;
 
   private:
     /**