xref: /openbmc/phosphor-health-monitor/health_metric.cpp (revision 2d4cbeb09fa719a4caa3d497f1a67bb572546313)
123f091e5SJagpal Singh Gill #include "health_metric.hpp"
223f091e5SJagpal Singh Gill 
323f091e5SJagpal Singh Gill #include <phosphor-logging/lg2.hpp>
423f091e5SJagpal Singh Gill 
5b94b122dSJagpal Singh Gill #include <cmath>
623f091e5SJagpal Singh Gill #include <numeric>
723f091e5SJagpal Singh Gill #include <unordered_map>
823f091e5SJagpal Singh Gill 
923f091e5SJagpal Singh Gill PHOSPHOR_LOG2_USING;
1023f091e5SJagpal Singh Gill 
1123f091e5SJagpal Singh Gill namespace phosphor::health::metric
1223f091e5SJagpal Singh Gill {
1323f091e5SJagpal Singh Gill 
1423f091e5SJagpal Singh Gill using association_t = std::tuple<std::string, std::string, std::string>;
1523f091e5SJagpal Singh Gill 
getPath(MType type,std::string name,SubType subType)16*2d4cbeb0SPatrick Williams auto HealthMetric::getPath(MType type, std::string name, SubType subType)
17*2d4cbeb0SPatrick Williams     -> std::string
1823f091e5SJagpal Singh Gill {
1923f091e5SJagpal Singh Gill     std::string path;
2023f091e5SJagpal Singh Gill     switch (subType)
2123f091e5SJagpal Singh Gill     {
2223f091e5SJagpal Singh Gill         case SubType::cpuTotal:
2323f091e5SJagpal Singh Gill         {
2423f091e5SJagpal Singh Gill             return std::string(BmcPath) + "/" + PathIntf::total_cpu;
2523f091e5SJagpal Singh Gill         }
2623f091e5SJagpal Singh Gill         case SubType::cpuKernel:
2723f091e5SJagpal Singh Gill         {
2823f091e5SJagpal Singh Gill             return std::string(BmcPath) + "/" + PathIntf::kernel_cpu;
2923f091e5SJagpal Singh Gill         }
3023f091e5SJagpal Singh Gill         case SubType::cpuUser:
3123f091e5SJagpal Singh Gill         {
3223f091e5SJagpal Singh Gill             return std::string(BmcPath) + "/" + PathIntf::user_cpu;
3323f091e5SJagpal Singh Gill         }
3423f091e5SJagpal Singh Gill         case SubType::memoryAvailable:
3523f091e5SJagpal Singh Gill         {
3623f091e5SJagpal Singh Gill             return std::string(BmcPath) + "/" + PathIntf::available_memory;
3723f091e5SJagpal Singh Gill         }
3823f091e5SJagpal Singh Gill         case SubType::memoryBufferedAndCached:
3923f091e5SJagpal Singh Gill         {
4023f091e5SJagpal Singh Gill             return std::string(BmcPath) + "/" +
4123f091e5SJagpal Singh Gill                    PathIntf::buffered_and_cached_memory;
4223f091e5SJagpal Singh Gill         }
4323f091e5SJagpal Singh Gill         case SubType::memoryFree:
4423f091e5SJagpal Singh Gill         {
4523f091e5SJagpal Singh Gill             return std::string(BmcPath) + "/" + PathIntf::free_memory;
4623f091e5SJagpal Singh Gill         }
4723f091e5SJagpal Singh Gill         case SubType::memoryShared:
4823f091e5SJagpal Singh Gill         {
4923f091e5SJagpal Singh Gill             return std::string(BmcPath) + "/" + PathIntf::shared_memory;
5023f091e5SJagpal Singh Gill         }
5123f091e5SJagpal Singh Gill         case SubType::memoryTotal:
5223f091e5SJagpal Singh Gill         {
5323f091e5SJagpal Singh Gill             return std::string(BmcPath) + "/" + PathIntf::total_memory;
5423f091e5SJagpal Singh Gill         }
5597582801SJagpal Singh Gill         case SubType::NA:
5623f091e5SJagpal Singh Gill         {
57658efd5fSPatrick Williams             if (type == MType::storage)
5897582801SJagpal Singh Gill             {
5997582801SJagpal Singh Gill                 static constexpr auto nameDelimiter = "_";
6097582801SJagpal Singh Gill                 auto storageType = name.substr(
6197582801SJagpal Singh Gill                     name.find_last_of(nameDelimiter) + 1, name.length());
62ce8b5ae4SPatrick Williams                 std::ranges::for_each(storageType, [](auto& c) {
63ce8b5ae4SPatrick Williams                     c = std::tolower(c);
64ce8b5ae4SPatrick Williams                 });
6597582801SJagpal Singh Gill                 return std::string(BmcPath) + "/" + PathIntf::storage + "/" +
6697582801SJagpal Singh Gill                        storageType;
6723f091e5SJagpal Singh Gill             }
6897582801SJagpal Singh Gill             else
69dfe839fdSJagpal Singh Gill             {
7097582801SJagpal Singh Gill                 error("Invalid metric {SUBTYPE} for metric {TYPE}", "SUBTYPE",
7197582801SJagpal Singh Gill                       subType, "TYPE", type);
7297582801SJagpal Singh Gill                 return "";
7397582801SJagpal Singh Gill             }
74dfe839fdSJagpal Singh Gill         }
7523f091e5SJagpal Singh Gill         default:
7623f091e5SJagpal Singh Gill         {
7797582801SJagpal Singh Gill             error("Invalid metric {SUBTYPE}", "SUBTYPE", subType);
7823f091e5SJagpal Singh Gill             return "";
7923f091e5SJagpal Singh Gill         }
8023f091e5SJagpal Singh Gill     }
8123f091e5SJagpal Singh Gill }
8223f091e5SJagpal Singh Gill 
initProperties()8323f091e5SJagpal Singh Gill void HealthMetric::initProperties()
8423f091e5SJagpal Singh Gill {
8597582801SJagpal Singh Gill     switch (type)
8623f091e5SJagpal Singh Gill     {
8797582801SJagpal Singh Gill         case MType::cpu:
8823f091e5SJagpal Singh Gill         {
8923f091e5SJagpal Singh Gill             ValueIntf::unit(ValueIntf::Unit::Percent, true);
9023f091e5SJagpal Singh Gill             ValueIntf::minValue(0.0, true);
9123f091e5SJagpal Singh Gill             ValueIntf::maxValue(100.0, true);
9223f091e5SJagpal Singh Gill             break;
9323f091e5SJagpal Singh Gill         }
9497582801SJagpal Singh Gill         case MType::memory:
9597582801SJagpal Singh Gill         case MType::storage:
9623f091e5SJagpal Singh Gill         {
9723f091e5SJagpal Singh Gill             ValueIntf::unit(ValueIntf::Unit::Bytes, true);
9823f091e5SJagpal Singh Gill             ValueIntf::minValue(0.0, true);
9997582801SJagpal Singh Gill             break;
10097582801SJagpal Singh Gill         }
10197582801SJagpal Singh Gill         case MType::inode:
10297582801SJagpal Singh Gill         case MType::unknown:
10397582801SJagpal Singh Gill         default:
10497582801SJagpal Singh Gill         {
10597582801SJagpal Singh Gill             throw std::invalid_argument("Invalid metric type");
10623f091e5SJagpal Singh Gill         }
10723f091e5SJagpal Singh Gill     }
108c5b18bcaSJagpal Singh Gill     ValueIntf::value(std::numeric_limits<double>::quiet_NaN(), true);
10923f091e5SJagpal Singh Gill 
110658efd5fSPatrick Williams     using bound_map_t = std::map<Bound, double>;
111658efd5fSPatrick Williams     std::map<Type, bound_map_t> thresholds;
11223f091e5SJagpal Singh Gill     for (const auto& [key, value] : config.thresholds)
11323f091e5SJagpal Singh Gill     {
114658efd5fSPatrick Williams         auto type = std::get<Type>(key);
115658efd5fSPatrick Williams         auto bound = std::get<Bound>(key);
11623f091e5SJagpal Singh Gill         auto threshold = thresholds.find(type);
11723f091e5SJagpal Singh Gill         if (threshold == thresholds.end())
11823f091e5SJagpal Singh Gill         {
11923f091e5SJagpal Singh Gill             bound_map_t bounds;
1206a3884a4SJagpal Singh Gill             bounds.emplace(bound, std::numeric_limits<double>::quiet_NaN());
12123f091e5SJagpal Singh Gill             thresholds.emplace(type, bounds);
12223f091e5SJagpal Singh Gill         }
12323f091e5SJagpal Singh Gill         else
12423f091e5SJagpal Singh Gill         {
12523f091e5SJagpal Singh Gill             threshold->second.emplace(bound, value.value);
12623f091e5SJagpal Singh Gill         }
12723f091e5SJagpal Singh Gill     }
128c5b18bcaSJagpal Singh Gill     ThresholdIntf::value(thresholds, true);
12923f091e5SJagpal Singh Gill }
13023f091e5SJagpal Singh Gill 
didThresholdViolate(ThresholdIntf::Bound bound,double thresholdValue,double value)13155fb0c96SJagpal Singh Gill bool didThresholdViolate(ThresholdIntf::Bound bound, double thresholdValue,
13255fb0c96SJagpal Singh Gill                          double value)
13355fb0c96SJagpal Singh Gill {
13455fb0c96SJagpal Singh Gill     switch (bound)
13555fb0c96SJagpal Singh Gill     {
13655fb0c96SJagpal Singh Gill         case ThresholdIntf::Bound::Lower:
13755fb0c96SJagpal Singh Gill         {
13855fb0c96SJagpal Singh Gill             return (value < thresholdValue);
13955fb0c96SJagpal Singh Gill         }
14055fb0c96SJagpal Singh Gill         case ThresholdIntf::Bound::Upper:
14155fb0c96SJagpal Singh Gill         {
14255fb0c96SJagpal Singh Gill             return (value > thresholdValue);
14355fb0c96SJagpal Singh Gill         }
14455fb0c96SJagpal Singh Gill         default:
14555fb0c96SJagpal Singh Gill         {
14667b8ebe0SPatrick Williams             error("Invalid threshold bound {BOUND}", "BOUND", bound);
14755fb0c96SJagpal Singh Gill             return false;
14855fb0c96SJagpal Singh Gill         }
14955fb0c96SJagpal Singh Gill     }
15055fb0c96SJagpal Singh Gill }
15155fb0c96SJagpal Singh Gill 
checkThreshold(Type type,Bound bound,MValue value)152658efd5fSPatrick Williams void HealthMetric::checkThreshold(Type type, Bound bound, MValue value)
15323f091e5SJagpal Singh Gill {
15423f091e5SJagpal Singh Gill     auto threshold = std::make_tuple(type, bound);
15523f091e5SJagpal Singh Gill     auto thresholds = ThresholdIntf::value();
15623f091e5SJagpal Singh Gill 
15723f091e5SJagpal Singh Gill     if (thresholds.contains(type) && thresholds[type].contains(bound))
15823f091e5SJagpal Singh Gill     {
1596a3884a4SJagpal Singh Gill         auto tConfig = config.thresholds.at(threshold);
1606a3884a4SJagpal Singh Gill         auto thresholdValue = tConfig.value / 100 * value.total;
1616a3884a4SJagpal Singh Gill         thresholds[type][bound] = thresholdValue;
1626a3884a4SJagpal Singh Gill         ThresholdIntf::value(thresholds);
16323f091e5SJagpal Singh Gill         auto assertions = ThresholdIntf::asserted();
1646a3884a4SJagpal Singh Gill         if (didThresholdViolate(bound, thresholdValue, value.current))
16523f091e5SJagpal Singh Gill         {
16623f091e5SJagpal Singh Gill             if (!assertions.contains(threshold))
16723f091e5SJagpal Singh Gill             {
16823f091e5SJagpal Singh Gill                 assertions.insert(threshold);
16923f091e5SJagpal Singh Gill                 ThresholdIntf::asserted(assertions);
1706a3884a4SJagpal Singh Gill                 ThresholdIntf::assertionChanged(type, bound, true,
1716a3884a4SJagpal Singh Gill                                                 value.current);
17223f091e5SJagpal Singh Gill                 if (tConfig.log)
17323f091e5SJagpal Singh Gill                 {
17423f091e5SJagpal Singh Gill                     error(
17523f091e5SJagpal Singh Gill                         "ASSERT: Health Metric {METRIC} crossed {TYPE} upper threshold",
17667b8ebe0SPatrick Williams                         "METRIC", config.name, "TYPE", type);
17723f091e5SJagpal Singh Gill                     startUnit(bus, tConfig.target);
17823f091e5SJagpal Singh Gill                 }
17923f091e5SJagpal Singh Gill             }
18023f091e5SJagpal Singh Gill             return;
18123f091e5SJagpal Singh Gill         }
18223f091e5SJagpal Singh Gill         else if (assertions.contains(threshold))
18323f091e5SJagpal Singh Gill         {
18423f091e5SJagpal Singh Gill             assertions.erase(threshold);
18523f091e5SJagpal Singh Gill             ThresholdIntf::asserted(assertions);
1866a3884a4SJagpal Singh Gill             ThresholdIntf::assertionChanged(type, bound, false, value.current);
18723f091e5SJagpal Singh Gill             if (config.thresholds.find(threshold)->second.log)
18823f091e5SJagpal Singh Gill             {
18923f091e5SJagpal Singh Gill                 info(
19023f091e5SJagpal Singh Gill                     "DEASSERT: Health Metric {METRIC} is below {TYPE} upper threshold",
19167b8ebe0SPatrick Williams                     "METRIC", config.name, "TYPE", type);
19223f091e5SJagpal Singh Gill             }
19323f091e5SJagpal Singh Gill         }
19423f091e5SJagpal Singh Gill     }
19523f091e5SJagpal Singh Gill }
19623f091e5SJagpal Singh Gill 
checkThresholds(MValue value)1976a3884a4SJagpal Singh Gill void HealthMetric::checkThresholds(MValue value)
19823f091e5SJagpal Singh Gill {
19923f091e5SJagpal Singh Gill     if (!ThresholdIntf::value().empty())
20023f091e5SJagpal Singh Gill     {
201658efd5fSPatrick Williams         for (auto type : {Type::HardShutdown, Type::SoftShutdown,
202658efd5fSPatrick Williams                           Type::PerformanceLoss, Type::Critical, Type::Warning})
20323f091e5SJagpal Singh Gill         {
204658efd5fSPatrick Williams             checkThreshold(type, Bound::Lower, value);
205658efd5fSPatrick Williams             checkThreshold(type, Bound::Upper, value);
20623f091e5SJagpal Singh Gill         }
20723f091e5SJagpal Singh Gill     }
20823f091e5SJagpal Singh Gill }
20923f091e5SJagpal Singh Gill 
shouldNotify(MValue value)210b94b122dSJagpal Singh Gill auto HealthMetric::shouldNotify(MValue value) -> bool
211b94b122dSJagpal Singh Gill {
212b94b122dSJagpal Singh Gill     if (std::isnan(value.current))
213b94b122dSJagpal Singh Gill     {
214b94b122dSJagpal Singh Gill         return true;
215b94b122dSJagpal Singh Gill     }
216ce8b5ae4SPatrick Williams     auto changed = std::abs(
217ce8b5ae4SPatrick Williams         (value.current - lastNotifiedValue) / lastNotifiedValue * 100.0);
218a102762bSJagpal Singh Gill     if (changed >= config.hysteresis)
219b94b122dSJagpal Singh Gill     {
220b94b122dSJagpal Singh Gill         lastNotifiedValue = value.current;
221b94b122dSJagpal Singh Gill         return true;
222b94b122dSJagpal Singh Gill     }
223b94b122dSJagpal Singh Gill     return false;
224b94b122dSJagpal Singh Gill }
225b94b122dSJagpal Singh Gill 
update(MValue value)22623f091e5SJagpal Singh Gill void HealthMetric::update(MValue value)
22723f091e5SJagpal Singh Gill {
228b94b122dSJagpal Singh Gill     ValueIntf::value(value.current, !shouldNotify(value));
2298fd4df2cSJagpal Singh Gill 
2308fd4df2cSJagpal Singh Gill     // Maintain window size for threshold calculation
23123f091e5SJagpal Singh Gill     if (history.size() >= config.windowSize)
23223f091e5SJagpal Singh Gill     {
23323f091e5SJagpal Singh Gill         history.pop_front();
23423f091e5SJagpal Singh Gill     }
2356a3884a4SJagpal Singh Gill     history.push_back(value.current);
23623f091e5SJagpal Singh Gill 
23723f091e5SJagpal Singh Gill     if (history.size() < config.windowSize)
23823f091e5SJagpal Singh Gill     {
23923f091e5SJagpal Singh Gill         // Wait for the metric to have enough samples to calculate average
24023f091e5SJagpal Singh Gill         return;
24123f091e5SJagpal Singh Gill     }
24223f091e5SJagpal Singh Gill 
243ce8b5ae4SPatrick Williams     double average =
244ce8b5ae4SPatrick Williams         (std::accumulate(history.begin(), history.end(), 0.0)) / history.size();
2458fd4df2cSJagpal Singh Gill     value.current = average;
2466a3884a4SJagpal Singh Gill     checkThresholds(value);
24723f091e5SJagpal Singh Gill }
24823f091e5SJagpal Singh Gill 
create(const paths_t & bmcPaths)24923f091e5SJagpal Singh Gill void HealthMetric::create(const paths_t& bmcPaths)
25023f091e5SJagpal Singh Gill {
25123f091e5SJagpal Singh Gill     info("Create Health Metric: {METRIC}", "METRIC", config.name);
25223f091e5SJagpal Singh Gill     initProperties();
25323f091e5SJagpal Singh Gill 
25423f091e5SJagpal Singh Gill     std::vector<association_t> associations;
25523f091e5SJagpal Singh Gill     static constexpr auto forwardAssociation = "measuring";
25623f091e5SJagpal Singh Gill     static constexpr auto reverseAssociation = "measured_by";
25723f091e5SJagpal Singh Gill     for (const auto& bmcPath : bmcPaths)
25823f091e5SJagpal Singh Gill     {
25923f091e5SJagpal Singh Gill         /*
26023f091e5SJagpal Singh Gill          * This metric is "measuring" the health for the BMC at bmcPath
26123f091e5SJagpal Singh Gill          * The BMC at bmcPath is "measured_by" this metric.
26223f091e5SJagpal Singh Gill          */
26323f091e5SJagpal Singh Gill         associations.push_back(
26423f091e5SJagpal Singh Gill             {forwardAssociation, reverseAssociation, bmcPath});
26523f091e5SJagpal Singh Gill     }
26623f091e5SJagpal Singh Gill     AssociationIntf::associations(associations);
26723f091e5SJagpal Singh Gill }
26823f091e5SJagpal Singh Gill 
26923f091e5SJagpal Singh Gill } // namespace phosphor::health::metric
270