blob: ae7d5938c682bda9d98f1dd4aa8a38041ff6e421 [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 */
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800106 metrics[config.subType]->update(MValue(activePercValue, 100));
Jagpal Singh Gill79434b52023-12-10 15:33:05 -0800107 }
108 return true;
109}
110
111auto 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 {
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800152 memoryValues[MetricIntf::SubType::memoryShared] += value;
Jagpal Singh Gill79434b52023-12-10 15:33:05 -0800153 }
154 }
155
156 for (auto& config : configs)
157 {
Jagpal Singh Gillda72a8c2024-02-23 18:09:20 -0800158 // Convert kB to Bytes
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800159 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));
Jagpal Singh Gill79434b52023-12-10 15:33:05 -0800165 }
166 return true;
167}
168
169auto 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 }
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800181 double value = buffer.f_bfree * buffer.f_frsize;
Jagpal Singh Gill159c2882024-02-22 14:43:51 -0800182 double total = buffer.f_blocks * buffer.f_frsize;
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800183 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));
Jagpal Singh Gill79434b52023-12-10 15:33:05 -0800187 }
188 return true;
189}
190
191void 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
228void 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