blob: 1fc0757075de4a6a6722ba9df8cc1ed1d47b1f98 [file] [log] [blame]
Jagpal Singh Gill79434b52023-12-10 15:33:05 -08001#include "health_metric_collection.hpp"
2
3#include <phosphor-logging/lg2.hpp>
4
5#include <fstream>
6#include <numeric>
7#include <unordered_map>
8
9extern "C"
10{
11#include <sys/statvfs.h>
12}
13
14PHOSPHOR_LOG2_USING;
15
16namespace phosphor::health::metric::collection
17{
18
19auto 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
112auto 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
171auto 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 / 1024);
184 double available = buffer.f_bfree * (buffer.f_frsize / 1024);
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
195void 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
232void 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