Better exception handling in the analyzer

Give the ability to continue creating a PEL with FFDC if there is an
exception in the analysis portion of the analyzer.

Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
Change-Id: I2de281a7c974cfc80779a861a12cb2b9c99e0491
diff --git a/analyzer/analyzer_main.cpp b/analyzer/analyzer_main.cpp
index 25f7005..b89124e 100644
--- a/analyzer/analyzer_main.cpp
+++ b/analyzer/analyzer_main.cpp
@@ -157,13 +157,27 @@
         {
             if (attnFound)
             {
-                // Resolve the root cause attention.
-                RasDataParser rasData{};
-                rasData.getResolution(rootCause)->resolve(servData);
+                try
+                {
+                    // Resolve the root cause attention.
+                    RasDataParser rasData{};
+                    rasData.getResolution(rootCause)->resolve(servData);
+                }
+                catch (const std::exception& e)
+                {
+                    trace::err("Exception caught during root cause analysis");
+                    trace::err(e.what());
+
+                    // We'll still want to create a PEL for the FFDC, but
+                    // since the analysis failed, we need to callout Level 2
+                    // Support.
+                    servData.calloutProcedure(callout::Procedure::NEXTLVL,
+                                              callout::Priority::HIGH);
+                }
             }
             else
             {
-                // Analysis failed so apply the Level 2 Support resolution.
+                // Analysis failed so callout the Level 2 Support.
                 servData.calloutProcedure(callout::Procedure::NEXTLVL,
                                           callout::Priority::HIGH);
             }
diff --git a/analyzer/plugins/plugin.hpp b/analyzer/plugins/plugin.hpp
index c5db3f0..501005c 100644
--- a/analyzer/plugins/plugin.hpp
+++ b/analyzer/plugins/plugin.hpp
@@ -4,6 +4,7 @@
 
 #include <analyzer/service_data.hpp>
 #include <hei_chip.hpp>
+#include <util/trace.hpp>
 
 #include <functional>
 #include <map>
@@ -107,7 +108,20 @@
     PluginFunction get(libhei::ChipType_t i_type,
                        const std::string& i_name) const
     {
-        return iv_map.at(i_type).at(i_name);
+        PluginFunction func;
+
+        try
+        {
+            func = iv_map.at(i_type).at(i_name);
+        }
+        catch (const std::out_of_range& e)
+        {
+            trace::err("Plugin not defined: i_type=0x%08x i_name=%s", i_type,
+                       i_name.c_str());
+            throw; // caught later downstream
+        }
+
+        return func;
     }
 };
 
diff --git a/analyzer/ras-data/ras-data-parser.cpp b/analyzer/ras-data/ras-data-parser.cpp
index 861283b..da8fec0 100644
--- a/analyzer/ras-data/ras-data-parser.cpp
+++ b/analyzer/ras-data/ras-data-parser.cpp
@@ -16,11 +16,34 @@
 std::shared_ptr<Resolution>
     RasDataParser::getResolution(const libhei::Signature& i_signature)
 {
-    const auto data = iv_dataFiles.at(i_signature.getChip().getType());
+    nlohmann::json data;
+
+    try
+    {
+        data = iv_dataFiles.at(i_signature.getChip().getType());
+    }
+    catch (const std::out_of_range& e)
+    {
+        trace::err("No RAS data defined for chip type: 0x%08x",
+                   i_signature.getChip().getType());
+        throw; // caught later downstream
+    }
 
     const auto action = parseSignature(data, i_signature);
 
-    return parseAction(data, action);
+    std::shared_ptr<Resolution> resolution;
+
+    try
+    {
+        resolution = parseAction(data, action);
+    }
+    catch (...)
+    {
+        trace::err("Unable to get resolution for action: %s", action.c_str());
+        throw; // caught later downstream
+    }
+
+    return resolution;
 }
 
 //------------------------------------------------------------------------------
@@ -46,15 +69,23 @@
         std::ifstream file{path};
         assert(file.good()); // The file must be readable.
 
-        // Parse the JSON.
-        auto schema = nlohmann::json::parse(file);
+        try
+        {
+            // Parse the JSON.
+            auto schema = nlohmann::json::parse(file);
 
-        // Get the schema version.
-        auto version = schema.at("version").get<unsigned int>();
+            // Get the schema version.
+            auto version = schema.at("version").get<unsigned int>();
 
-        // Keep track of the schemas.
-        auto ret = schemaFiles.emplace(version, schema);
-        assert(ret.second); // Should not have duplicate entries
+            // Keep track of the schemas.
+            auto ret = schemaFiles.emplace(version, schema);
+            assert(ret.second); // Should not have duplicate entries
+        }
+        catch (...)
+        {
+            trace::err("Failed to parse file: %s", path.string().c_str());
+            throw; // caught later downstream
+        }
     }
 
     // Get the RAS data files from the package `data` subdirectory.
@@ -72,27 +103,35 @@
         std::ifstream file{path};
         assert(file.good()); // The file must be readable.
 
-        // Parse the JSON.
-        const auto data = nlohmann::json::parse(file);
+        try
+        {
+            // Parse the JSON.
+            const auto data = nlohmann::json::parse(file);
 
-        // Get the data version.
-        auto version = data.at("version").get<unsigned int>();
+            // Get the data version.
+            auto version = data.at("version").get<unsigned int>();
 
-        // Get the schema for this file.
-        auto schema = schemaFiles.at(version);
+            // Get the schema for this file.
+            auto schema = schemaFiles.at(version);
 
-        // Validate the data against the schema.
-        assert(util::validateJson(schema, data));
+            // Validate the data against the schema.
+            assert(util::validateJson(schema, data));
 
-        // Get the chip model/EC level from the data. The value is currently
-        // stored as a string representation of the hex value. So it will have
-        // to be converted to an integer.
-        libhei::ChipType_t chipType =
-            std::stoul(data.at("model_ec").get<std::string>(), 0, 16);
+            // Get the chip model/EC level from the data. The value is currently
+            // stored as a string representation of the hex value. So it will
+            // have to be converted to an integer.
+            libhei::ChipType_t chipType =
+                std::stoul(data.at("model_ec").get<std::string>(), 0, 16);
 
-        // So far, so good. Add the entry.
-        auto ret = iv_dataFiles.emplace(chipType, data);
-        assert(ret.second); // Should not have duplicate entries
+            // So far, so good. Add the entry.
+            auto ret = iv_dataFiles.emplace(chipType, data);
+            assert(ret.second); // Should not have duplicate entries
+        }
+        catch (...)
+        {
+            trace::err("Failed to parse file: %s", path.string().c_str());
+            throw; // caught later downstream
+        }
     }
 }
 
@@ -112,8 +151,22 @@
     sprintf(buf, "%02x", i_signature.getInstance());
     std::string inst{buf};
 
+    std::string action;
+
+    try
+    {
+        action =
+            i_data.at("signatures").at(id).at(bit).at(inst).get<std::string>();
+    }
+    catch (const std::out_of_range& e)
+    {
+        trace::err("No action defined for signature: %s %s %s", id.c_str(),
+                   bit.c_str(), inst.c_str());
+        throw; // caught later downstream
+    }
+
     // Return the action.
-    return i_data.at("signatures").at(id).at(bit).at(inst).get<std::string>();
+    return action;
 }
 
 //------------------------------------------------------------------------------
diff --git a/analyzer/service_data.cpp b/analyzer/service_data.cpp
index 2a7d5f5..28ef329 100644
--- a/analyzer/service_data.cpp
+++ b/analyzer/service_data.cpp
@@ -166,9 +166,20 @@
 
     // A map to determine the priority order. All of the medium priorities,
     // including the medium group priorities, are all the same level.
+    // clang-format off
     static const std::map<std::string, unsigned int> m = {
-        {"H", 3}, {"M", 2}, {"A", 2}, {"B", 2}, {"C", 2}, {"L", 1},
+        {callout::getString(callout::Priority::HIGH),  3},
+        {callout::getString(callout::Priority::MED),   2},
+        {callout::getString(callout::Priority::MED_A), 2},
+        {callout::getString(callout::Priority::MED_B), 2},
+        {callout::getString(callout::Priority::MED_C), 2},
+        {callout::getString(callout::Priority::LOW),   1},
     };
+    // clang-format on
+
+    // The new callout must contain a valid priority.
+    assert(i_callout.contains("Priority") &&
+           m.contains(i_callout.at("Priority")));
 
     bool addCallout = true;