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(MValue(activePercValue, 100)); 107 } 108 return true; 109 } 110 111 auto HealthMetricCollection::readMemory() -> bool 112 { 113 constexpr auto procMeminfo = "/proc/meminfo"; 114 std::ifstream memInfo(procMeminfo); 115 if (!memInfo.is_open()) 116 { 117 error("Unable to open {PATH} for reading Memory stats", "PATH", 118 procMeminfo); 119 return false; 120 } 121 std::string line; 122 std::unordered_map<MetricIntf::SubType, double> memoryValues; 123 124 while (std::getline(memInfo, line)) 125 { 126 std::string name; 127 double value; 128 std::istringstream iss(line); 129 130 if (!(iss >> name >> value)) 131 { 132 continue; 133 } 134 if (name.starts_with("MemAvailable")) 135 { 136 memoryValues[MetricIntf::SubType::memoryAvailable] = value; 137 } 138 else if (name.starts_with("MemFree")) 139 { 140 memoryValues[MetricIntf::SubType::memoryFree] = value; 141 } 142 else if (name.starts_with("Buffers") || name.starts_with("Cached")) 143 { 144 memoryValues[MetricIntf::SubType::memoryBufferedAndCached] += value; 145 } 146 else if (name.starts_with("MemTotal")) 147 { 148 memoryValues[MetricIntf::SubType::memoryTotal] = value; 149 } 150 else if (name.starts_with("Shmem")) 151 { 152 memoryValues[MetricIntf::SubType::memoryShared] += value; 153 } 154 } 155 156 for (auto& config : configs) 157 { 158 // Convert kB to Bytes 159 auto value = memoryValues.at(config.subType) * 1024; 160 auto total = memoryValues.at(MetricIntf::SubType::memoryTotal) * 1024; 161 debug("Memory Metric {SUBTYPE}: {VALUE}, {TOTAL}", "SUBTYPE", 162 std::to_underlying(config.subType), "VALUE", value, "TOTAL", 163 total); 164 metrics[config.subType]->update(MValue(value, total)); 165 } 166 return true; 167 } 168 169 auto HealthMetricCollection::readStorage() -> bool 170 { 171 for (auto& config : configs) 172 { 173 struct statvfs buffer; 174 if (statvfs(config.path.c_str(), &buffer) != 0) 175 { 176 auto e = errno; 177 error("Error from statvfs: {ERROR}, path: {PATH}", "ERROR", 178 strerror(e), "PATH", config.path); 179 continue; 180 } 181 double value = buffer.f_bfree * buffer.f_frsize; 182 double total = buffer.f_blocks * buffer.f_frsize; 183 debug("Storage Metric {SUBTYPE}: {VALUE}, {TOTAL}", "SUBTYPE", 184 std::to_underlying(config.subType), "VALUE", value, "TOTAL", 185 total); 186 metrics[config.subType]->update(MValue(value, total)); 187 } 188 return true; 189 } 190 191 void HealthMetricCollection::read() 192 { 193 switch (type) 194 { 195 case MetricIntf::Type::cpu: 196 { 197 if (!readCPU()) 198 { 199 error("Failed to read CPU health metric"); 200 } 201 break; 202 } 203 case MetricIntf::Type::memory: 204 { 205 if (!readMemory()) 206 { 207 error("Failed to read memory health metric"); 208 } 209 break; 210 } 211 case MetricIntf::Type::storage: 212 { 213 if (!readStorage()) 214 { 215 error("Failed to read storage health metric"); 216 } 217 break; 218 } 219 default: 220 { 221 error("Unknown health metric type {TYPE}", "TYPE", 222 std::to_underlying(type)); 223 break; 224 } 225 } 226 } 227 228 void HealthMetricCollection::create(const MetricIntf::paths_t& bmcPaths) 229 { 230 metrics.clear(); 231 232 for (auto& config : configs) 233 { 234 /* TODO: Remove this after adding iNode support */ 235 if (config.subType == MetricIntf::SubType::NA) 236 { 237 continue; 238 } 239 metrics[config.subType] = std::make_unique<MetricIntf::HealthMetric>( 240 bus, type, config, bmcPaths); 241 } 242 } 243 244 } // namespace phosphor::health::metric::collection 245