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