blob: 552a2c3af63abfc73611af7df7e51f85bfc7197b [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;
Jagpal Singh Gillda72a8c2024-02-23 18:09:20 -0800162 // Convert kB to Bytes
163 absoluteValue = absoluteValue * 1024;
Jagpal Singh Gill79434b52023-12-10 15:33:05 -0800164 debug("Memory Metric {SUBTYPE}: {VALUE}, {PERCENT}", "SUBTYPE",
165 std::to_underlying(config.subType), "VALUE", absoluteValue,
166 "PERCENT", percentValue);
167 metrics[config.subType]->update(MValue(absoluteValue, percentValue));
168 }
169 return true;
170}
171
172auto HealthMetricCollection::readStorage() -> bool
173{
174 for (auto& config : configs)
175 {
176 struct statvfs buffer;
177 if (statvfs(config.path.c_str(), &buffer) != 0)
178 {
179 auto e = errno;
180 error("Error from statvfs: {ERROR}, path: {PATH}", "ERROR",
181 strerror(e), "PATH", config.path);
182 continue;
183 }
Jagpal Singh Gill159c2882024-02-22 14:43:51 -0800184 double total = buffer.f_blocks * buffer.f_frsize;
185 double available = buffer.f_bfree * buffer.f_frsize;
Jagpal Singh Gill79434b52023-12-10 15:33:05 -0800186 double availablePercent = ((available / total) * 100);
187
188 debug("Storage Metric {SUBTYPE}: {TOTAL} {AVAIL} {AVAIL_PERCENT}",
189 "SUBTYPE", std::to_underlying(config.subType), "TOTAL", total,
190 "AVAIL", available, "AVAIL_PERCENT", availablePercent);
191 metrics[config.subType]->update(MValue(available, availablePercent));
192 }
193 return true;
194}
195
196void HealthMetricCollection::read()
197{
198 switch (type)
199 {
200 case MetricIntf::Type::cpu:
201 {
202 if (!readCPU())
203 {
204 error("Failed to read CPU health metric");
205 }
206 break;
207 }
208 case MetricIntf::Type::memory:
209 {
210 if (!readMemory())
211 {
212 error("Failed to read memory health metric");
213 }
214 break;
215 }
216 case MetricIntf::Type::storage:
217 {
218 if (!readStorage())
219 {
220 error("Failed to read storage health metric");
221 }
222 break;
223 }
224 default:
225 {
226 error("Unknown health metric type {TYPE}", "TYPE",
227 std::to_underlying(type));
228 break;
229 }
230 }
231}
232
233void HealthMetricCollection::create(const MetricIntf::paths_t& bmcPaths)
234{
235 metrics.clear();
236
237 for (auto& config : configs)
238 {
239 /* TODO: Remove this after adding iNode support */
240 if (config.subType == MetricIntf::SubType::NA)
241 {
242 continue;
243 }
244 metrics[config.subType] = std::make_unique<MetricIntf::HealthMetric>(
245 bus, type, config, bmcPaths);
246 }
247}
248
249} // namespace phosphor::health::metric::collection