1 #include "health_metric_collection.hpp" 2 3 #include <phosphor-logging/lg2.hpp> 4 5 #include <fstream> 6 #include <numeric> 7 #include <unordered_map> 8 9 extern "C" 10 { 11 #include <sys/statvfs.h> 12 } 13 14 PHOSPHOR_LOG2_USING; 15 16 namespace phosphor::health::metric::collection 17 { 18 19 auto HealthMetricCollection::readCPU() -> bool 20 { 21 enum CPUStatsIndex 22 { 23 userIndex = 0, 24 niceIndex, 25 systemIndex, 26 idleIndex, 27 iowaitIndex, 28 irqIndex, 29 softirqIndex, 30 stealIndex, 31 guestUserIndex, 32 guestNiceIndex, 33 maxIndex 34 }; 35 constexpr auto procStat = "/proc/stat"; 36 std::ifstream fileStat(procStat); 37 if (!fileStat.is_open()) 38 { 39 error("Unable to open {PATH} for reading CPU stats", "PATH", procStat); 40 return false; 41 } 42 43 std::string firstLine, labelName; 44 std::size_t timeData[CPUStatsIndex::maxIndex] = {0}; 45 46 std::getline(fileStat, firstLine); 47 std::stringstream ss(firstLine); 48 ss >> labelName; 49 50 if (labelName.compare("cpu")) 51 { 52 error("CPU data not available"); 53 return false; 54 } 55 56 for (auto idx = 0; idx < CPUStatsIndex::maxIndex; idx++) 57 { 58 if (!(ss >> timeData[idx])) 59 { 60 error("CPU data not correct"); 61 return false; 62 } 63 } 64 65 for (auto& config : configs) 66 { 67 uint64_t activeTime = 0, activeTimeDiff = 0, totalTime = 0, 68 totalTimeDiff = 0; 69 double activePercValue = 0; 70 71 if (config.subType == MetricIntf::SubType::cpuTotal) 72 { 73 activeTime = timeData[CPUStatsIndex::userIndex] + 74 timeData[CPUStatsIndex::niceIndex] + 75 timeData[CPUStatsIndex::systemIndex] + 76 timeData[CPUStatsIndex::irqIndex] + 77 timeData[CPUStatsIndex::softirqIndex] + 78 timeData[CPUStatsIndex::stealIndex] + 79 timeData[CPUStatsIndex::guestUserIndex] + 80 timeData[CPUStatsIndex::guestNiceIndex]; 81 } 82 else if (config.subType == MetricIntf::SubType::cpuKernel) 83 { 84 activeTime = timeData[CPUStatsIndex::systemIndex]; 85 } 86 else if (config.subType == MetricIntf::SubType::cpuUser) 87 { 88 activeTime = timeData[CPUStatsIndex::userIndex]; 89 } 90 91 totalTime = std::accumulate(std::begin(timeData), std::end(timeData), 92 decltype(totalTime){0}); 93 94 activeTimeDiff = activeTime - preActiveTime[config.subType]; 95 totalTimeDiff = totalTime - preTotalTime[config.subType]; 96 97 /* Store current active and total time for next calculation */ 98 preActiveTime[config.subType] = activeTime; 99 preTotalTime[config.subType] = totalTime; 100 101 activePercValue = (100.0 * activeTimeDiff) / totalTimeDiff; 102 debug("CPU Metric {SUBTYPE}: {VALUE}", "SUBTYPE", 103 std::to_underlying(config.subType), "VALUE", 104 (double)activePercValue); 105 /* For CPU, both user and monitor uses percentage values */ 106 metrics[config.subType]->update( 107 MValue(activePercValue, activePercValue)); 108 } 109 return true; 110 } 111 112 auto HealthMetricCollection::readMemory() -> bool 113 { 114 constexpr auto procMeminfo = "/proc/meminfo"; 115 std::ifstream memInfo(procMeminfo); 116 if (!memInfo.is_open()) 117 { 118 error("Unable to open {PATH} for reading Memory stats", "PATH", 119 procMeminfo); 120 return false; 121 } 122 std::string line; 123 std::unordered_map<MetricIntf::SubType, double> memoryValues; 124 125 while (std::getline(memInfo, line)) 126 { 127 std::string name; 128 double value; 129 std::istringstream iss(line); 130 131 if (!(iss >> name >> value)) 132 { 133 continue; 134 } 135 if (name.starts_with("MemAvailable")) 136 { 137 memoryValues[MetricIntf::SubType::memoryAvailable] = value; 138 } 139 else if (name.starts_with("MemFree")) 140 { 141 memoryValues[MetricIntf::SubType::memoryFree] = value; 142 } 143 else if (name.starts_with("Buffers") || name.starts_with("Cached")) 144 { 145 memoryValues[MetricIntf::SubType::memoryBufferedAndCached] += value; 146 } 147 else if (name.starts_with("MemTotal")) 148 { 149 memoryValues[MetricIntf::SubType::memoryTotal] = value; 150 } 151 else if (name.starts_with("Shmem")) 152 { 153 memoryValues[MetricIntf::SubType::memoryShared] = value; 154 } 155 } 156 157 for (auto& config : configs) 158 { 159 auto absoluteValue = memoryValues.at(config.subType); 160 auto memoryTotal = memoryValues.at(MetricIntf::SubType::memoryTotal); 161 double percentValue = (memoryTotal - absoluteValue) / memoryTotal * 100; 162 absoluteValue = absoluteValue * 1000; 163 debug("Memory Metric {SUBTYPE}: {VALUE}, {PERCENT}", "SUBTYPE", 164 std::to_underlying(config.subType), "VALUE", absoluteValue, 165 "PERCENT", percentValue); 166 metrics[config.subType]->update(MValue(absoluteValue, percentValue)); 167 } 168 return true; 169 } 170 171 auto HealthMetricCollection::readStorage() -> bool 172 { 173 for (auto& config : configs) 174 { 175 struct statvfs buffer; 176 if (statvfs(config.path.c_str(), &buffer) != 0) 177 { 178 auto e = errno; 179 error("Error from statvfs: {ERROR}, path: {PATH}", "ERROR", 180 strerror(e), "PATH", config.path); 181 continue; 182 } 183 double total = buffer.f_blocks * buffer.f_frsize; 184 double available = buffer.f_bfree * buffer.f_frsize; 185 double availablePercent = ((available / total) * 100); 186 187 debug("Storage Metric {SUBTYPE}: {TOTAL} {AVAIL} {AVAIL_PERCENT}", 188 "SUBTYPE", std::to_underlying(config.subType), "TOTAL", total, 189 "AVAIL", available, "AVAIL_PERCENT", availablePercent); 190 metrics[config.subType]->update(MValue(available, availablePercent)); 191 } 192 return true; 193 } 194 195 void HealthMetricCollection::read() 196 { 197 switch (type) 198 { 199 case MetricIntf::Type::cpu: 200 { 201 if (!readCPU()) 202 { 203 error("Failed to read CPU health metric"); 204 } 205 break; 206 } 207 case MetricIntf::Type::memory: 208 { 209 if (!readMemory()) 210 { 211 error("Failed to read memory health metric"); 212 } 213 break; 214 } 215 case MetricIntf::Type::storage: 216 { 217 if (!readStorage()) 218 { 219 error("Failed to read storage health metric"); 220 } 221 break; 222 } 223 default: 224 { 225 error("Unknown health metric type {TYPE}", "TYPE", 226 std::to_underlying(type)); 227 break; 228 } 229 } 230 } 231 232 void HealthMetricCollection::create(const MetricIntf::paths_t& bmcPaths) 233 { 234 metrics.clear(); 235 236 for (auto& config : configs) 237 { 238 /* TODO: Remove this after adding iNode support */ 239 if (config.subType == MetricIntf::SubType::NA) 240 { 241 continue; 242 } 243 metrics[config.subType] = std::make_unique<MetricIntf::HealthMetric>( 244 bus, type, config, bmcPaths); 245 } 246 } 247 248 } // namespace phosphor::health::metric::collection 249