Add root cause filters for clock errors

Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
Change-Id: Ia99c31d985e0296b124bfc624b7990d0613dd2d4
diff --git a/analyzer/filter-root-cause.cpp b/analyzer/filter-root-cause.cpp
index 421c674..1820d09 100644
--- a/analyzer/filter-root-cause.cpp
+++ b/analyzer/filter-root-cause.cpp
@@ -11,6 +11,84 @@
 
 //------------------------------------------------------------------------------
 
+uint64_t __hash(unsigned int i_bytes, const std::string& i_str)
+{
+    // This hash is a simple "n*s[0] + (n-1)*s[1] + ... + s[n-1]" algorithm,
+    // where s[i] is a chunk from the input string the length of i_bytes.
+
+    // Currently only supporting 1-8 byte hashes.
+    assert(1 <= i_bytes && i_bytes <= sizeof(uint64_t));
+
+    // Start hashing each chunk.
+    uint64_t sumA = 0;
+    uint64_t sumB = 0;
+
+    // Iterate one chunk at a time.
+    for (unsigned int i = 0; i < i_str.size(); i += i_bytes)
+    {
+        // Combine each chunk into a single integer value. If we reach the end
+        // of the string, pad with null characters.
+        uint64_t chunk = 0;
+        for (unsigned int j = 0; j < i_bytes; j++)
+        {
+            chunk <<= 8;
+            chunk |= (i + j < i_str.size()) ? i_str[i + j] : '\0';
+        }
+
+        // Apply the simple hash.
+        sumA += chunk;
+        sumB += sumA;
+    }
+
+    // Mask off everything except the target number of bytes.
+    auto mask = std::numeric_limits<uint64_t>::max();
+    sumB &= mask >> ((sizeof(uint64_t) - i_bytes) * 8);
+
+    return sumB;
+}
+
+//------------------------------------------------------------------------------
+
+bool __findRcsOscError(const std::vector<libhei::Signature>& i_list,
+                       libhei::Signature& o_rootCause)
+{
+    // TODO: Consider returning all of them instead of one as root cause.
+    auto itr = std::find_if(i_list.begin(), i_list.end(), [&](const auto& t) {
+        return (__hash(2, "TP_LOCAL_FIR") == t.getId() &&
+                (42 == t.getBit() || 43 == t.getBit()));
+    });
+
+    if (i_list.end() != itr)
+    {
+        o_rootCause = *itr;
+        return true;
+    }
+
+    return false;
+}
+
+//------------------------------------------------------------------------------
+
+bool __findPllUnlock(const std::vector<libhei::Signature>& i_list,
+                     libhei::Signature& o_rootCause)
+{
+    // TODO: Consider returning all of them instead of one as root cause.
+    auto itr = std::find_if(i_list.begin(), i_list.end(), [&](const auto& t) {
+        return (__hash(2, "PLL_UNLOCK") == t.getId() &&
+                (0 == t.getBit() || 1 == t.getBit()));
+    });
+
+    if (i_list.end() != itr)
+    {
+        o_rootCause = *itr;
+        return true;
+    }
+
+    return false;
+}
+
+//------------------------------------------------------------------------------
+
 bool filterRootCause(const libhei::IsolationData& i_isoData,
                      libhei::Signature& o_rootCause)
 {
@@ -29,6 +107,15 @@
     });
     list.resize(std::distance(list.begin(), itr));
 
+    // Look for:
+    //   - any RCS OSC errors (always first)
+    //   - any PLL unlock attentions (always second)
+    if (__findRcsOscError(list, o_rootCause) ||
+        __findPllUnlock(list, o_rootCause))
+    {
+        return true;
+    }
+
     // TODO: This is a rudimentary filter that first looks for any recoverable
     //       attention, then any unit checkstop, and then any system checkstop.
     //       This is built on the premise that recoverable errors could be the