blob: d83f8d078ed62c4e6679375e11c0aabaeab8f6c3 [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;
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600102 debug("CPU Metric {SUBTYPE}: {VALUE}", "SUBTYPE", config.subType,
103 "VALUE", (double)activePercValue);
Jagpal Singh Gill79434b52023-12-10 15:33:05 -0800104 /* For CPU, both user and monitor uses percentage values */
Jagpal Singh Gill97582802024-02-27 13:59:11 -0800105 metrics[config.name]->update(MValue(activePercValue, 100));
Jagpal Singh Gill79434b52023-12-10 15:33:05 -0800106 }
107 return true;
108}
109
110auto 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 {
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800151 memoryValues[MetricIntf::SubType::memoryShared] += value;
Jagpal Singh Gill79434b52023-12-10 15:33:05 -0800152 }
153 }
154
155 for (auto& config : configs)
156 {
Jagpal Singh Gillda72a8c2024-02-23 18:09:20 -0800157 // Convert kB to Bytes
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800158 auto value = memoryValues.at(config.subType) * 1024;
159 auto total = memoryValues.at(MetricIntf::SubType::memoryTotal) * 1024;
160 debug("Memory Metric {SUBTYPE}: {VALUE}, {TOTAL}", "SUBTYPE",
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600161 config.subType, "VALUE", value, "TOTAL", total);
Jagpal Singh Gill97582802024-02-27 13:59:11 -0800162 metrics[config.name]->update(MValue(value, total));
Jagpal Singh Gill79434b52023-12-10 15:33:05 -0800163 }
164 return true;
165}
166
167auto 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 }
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800179 double value = buffer.f_bfree * buffer.f_frsize;
Jagpal Singh Gill159c2882024-02-22 14:43:51 -0800180 double total = buffer.f_blocks * buffer.f_frsize;
Jagpal Singh Gill6a3884a2024-02-24 18:08:23 -0800181 debug("Storage Metric {SUBTYPE}: {VALUE}, {TOTAL}", "SUBTYPE",
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600182 config.subType, "VALUE", value, "TOTAL", total);
Jagpal Singh Gill97582802024-02-27 13:59:11 -0800183 metrics[config.name]->update(MValue(value, total));
Jagpal Singh Gill79434b52023-12-10 15:33:05 -0800184 }
185 return true;
186}
187
188void 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 {
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600218 error("Unknown health metric type {TYPE}", "TYPE", type);
Jagpal Singh Gill79434b52023-12-10 15:33:05 -0800219 break;
220 }
221 }
222}
223
224void HealthMetricCollection::create(const MetricIntf::paths_t& bmcPaths)
225{
226 metrics.clear();
227
228 for (auto& config : configs)
229 {
Jagpal Singh Gill97582802024-02-27 13:59:11 -0800230 metrics[config.name] = std::make_unique<MetricIntf::HealthMetric>(
Jagpal Singh Gill79434b52023-12-10 15:33:05 -0800231 bus, type, config, bmcPaths);
232 }
233}
234
235} // namespace phosphor::health::metric::collection