11285115cSWilliam A. Kennington III // Copyright 2021 Google LLC
21285115cSWilliam A. Kennington III //
31285115cSWilliam A. Kennington III // Licensed under the Apache License, Version 2.0 (the "License");
41285115cSWilliam A. Kennington III // you may not use this file except in compliance with the License.
51285115cSWilliam A. Kennington III // You may obtain a copy of the License at
61285115cSWilliam A. Kennington III //
71285115cSWilliam A. Kennington III // http://www.apache.org/licenses/LICENSE-2.0
81285115cSWilliam A. Kennington III //
91285115cSWilliam A. Kennington III // Unless required by applicable law or agreed to in writing, software
101285115cSWilliam A. Kennington III // distributed under the License is distributed on an "AS IS" BASIS,
111285115cSWilliam A. Kennington III // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
121285115cSWilliam A. Kennington III // See the License for the specific language governing permissions and
131285115cSWilliam A. Kennington III // limitations under the License.
141285115cSWilliam A. Kennington III
151285115cSWilliam A. Kennington III #include "util.hpp"
161285115cSWilliam A. Kennington III
17b63d6314SMichael Shen #include <fcntl.h>
18b63d6314SMichael Shen #include <sys/mman.h>
19b63d6314SMichael Shen #include <sys/stat.h>
201285115cSWilliam A. Kennington III #include <unistd.h>
211285115cSWilliam A. Kennington III
221285115cSWilliam A. Kennington III #include <phosphor-logging/log.hpp>
23b63d6314SMichael Shen #include <sdbusplus/bus.hpp>
24b63d6314SMichael Shen #include <sdbusplus/message.hpp>
251285115cSWilliam A. Kennington III
261285115cSWilliam A. Kennington III #include <cmath>
271285115cSWilliam A. Kennington III #include <cstdlib>
281285115cSWilliam A. Kennington III #include <fstream>
291285115cSWilliam A. Kennington III #include <sstream>
301285115cSWilliam A. Kennington III #include <string>
311285115cSWilliam A. Kennington III #include <string_view>
32b63d6314SMichael Shen #include <unordered_map>
33b63d6314SMichael Shen #include <utility>
34b63d6314SMichael Shen #include <variant>
35b63d6314SMichael Shen #include <vector>
361285115cSWilliam A. Kennington III
371285115cSWilliam A. Kennington III namespace metric_blob
381285115cSWilliam A. Kennington III {
391285115cSWilliam A. Kennington III
401285115cSWilliam A. Kennington III using phosphor::logging::log;
411285115cSWilliam A. Kennington III using level = phosphor::logging::level;
421285115cSWilliam A. Kennington III
controlCharsToSpace(char c)431285115cSWilliam A. Kennington III char controlCharsToSpace(char c)
441285115cSWilliam A. Kennington III {
451285115cSWilliam A. Kennington III if (c < 32)
461285115cSWilliam A. Kennington III {
471285115cSWilliam A. Kennington III c = ' ';
481285115cSWilliam A. Kennington III }
491285115cSWilliam A. Kennington III return c;
501285115cSWilliam A. Kennington III }
511285115cSWilliam A. Kennington III
getTicksPerSec()521285115cSWilliam A. Kennington III long getTicksPerSec()
531285115cSWilliam A. Kennington III {
541285115cSWilliam A. Kennington III return sysconf(_SC_CLK_TCK);
551285115cSWilliam A. Kennington III }
561285115cSWilliam A. Kennington III
readFileThenGrepIntoString(const std::string_view fileName,const std::string_view grepStr)57b63d6314SMichael Shen std::string readFileThenGrepIntoString(const std::string_view fileName,
58b63d6314SMichael Shen const std::string_view grepStr)
591285115cSWilliam A. Kennington III {
601285115cSWilliam A. Kennington III std::stringstream ss;
611285115cSWilliam A. Kennington III std::ifstream ifs(fileName.data());
621285115cSWilliam A. Kennington III while (ifs.good())
631285115cSWilliam A. Kennington III {
641285115cSWilliam A. Kennington III std::string line;
651285115cSWilliam A. Kennington III std::getline(ifs, line);
66b63d6314SMichael Shen if (line.find(grepStr) != std::string::npos)
67b63d6314SMichael Shen {
681285115cSWilliam A. Kennington III ss << line;
69b63d6314SMichael Shen }
701285115cSWilliam A. Kennington III if (ifs.good())
711285115cSWilliam A. Kennington III ss << std::endl;
721285115cSWilliam A. Kennington III }
731285115cSWilliam A. Kennington III return ss.str();
741285115cSWilliam A. Kennington III }
751285115cSWilliam A. Kennington III
isNumericPath(const std::string_view path,int & value)761285115cSWilliam A. Kennington III bool isNumericPath(const std::string_view path, int& value)
771285115cSWilliam A. Kennington III {
781285115cSWilliam A. Kennington III size_t p = path.rfind('/');
791285115cSWilliam A. Kennington III if (p == std::string::npos)
801285115cSWilliam A. Kennington III {
811285115cSWilliam A. Kennington III return false;
821285115cSWilliam A. Kennington III }
831285115cSWilliam A. Kennington III int id = 0;
841285115cSWilliam A. Kennington III for (size_t i = p + 1; i < path.size(); ++i)
851285115cSWilliam A. Kennington III {
861285115cSWilliam A. Kennington III const char ch = path[i];
871285115cSWilliam A. Kennington III if (ch < '0' || ch > '9')
881285115cSWilliam A. Kennington III return false;
891285115cSWilliam A. Kennington III else
901285115cSWilliam A. Kennington III {
911285115cSWilliam A. Kennington III id = id * 10 + (ch - '0');
921285115cSWilliam A. Kennington III }
931285115cSWilliam A. Kennington III }
941285115cSWilliam A. Kennington III value = id;
951285115cSWilliam A. Kennington III return true;
961285115cSWilliam A. Kennington III }
971285115cSWilliam A. Kennington III
981285115cSWilliam A. Kennington III // Trims all control characters at the end of a string.
trimStringRight(std::string_view s)991285115cSWilliam A. Kennington III std::string trimStringRight(std::string_view s)
1001285115cSWilliam A. Kennington III {
1011285115cSWilliam A. Kennington III std::string ret(s.data());
1021285115cSWilliam A. Kennington III while (!ret.empty())
1031285115cSWilliam A. Kennington III {
1041285115cSWilliam A. Kennington III if (ret.back() <= 32)
1051285115cSWilliam A. Kennington III ret.pop_back();
1061285115cSWilliam A. Kennington III else
1071285115cSWilliam A. Kennington III break;
1081285115cSWilliam A. Kennington III }
1091285115cSWilliam A. Kennington III return ret;
1101285115cSWilliam A. Kennington III }
1111285115cSWilliam A. Kennington III
getCmdLine(const int pid)1121285115cSWilliam A. Kennington III std::string getCmdLine(const int pid)
1131285115cSWilliam A. Kennington III {
114*c66ebc35SPatrick Williams const std::string& cmdlinePath =
115*c66ebc35SPatrick Williams "/proc/" + std::to_string(pid) + "/cmdline";
1161285115cSWilliam A. Kennington III
117b63d6314SMichael Shen std::string cmdline = readFileThenGrepIntoString(cmdlinePath);
1181285115cSWilliam A. Kennington III for (size_t i = 0; i < cmdline.size(); ++i)
1191285115cSWilliam A. Kennington III {
1201285115cSWilliam A. Kennington III cmdline[i] = controlCharsToSpace(cmdline[i]);
1211285115cSWilliam A. Kennington III }
1221285115cSWilliam A. Kennington III
1231285115cSWilliam A. Kennington III // Trim empty strings
1241285115cSWilliam A. Kennington III cmdline = trimStringRight(cmdline);
1251285115cSWilliam A. Kennington III
1261285115cSWilliam A. Kennington III return cmdline;
1271285115cSWilliam A. Kennington III }
1281285115cSWilliam A. Kennington III
1291285115cSWilliam A. Kennington III // strtok is used in this function in order to avoid usage of <sstream>.
1301285115cSWilliam A. Kennington III // However, that would require us to create a temporary std::string.
parseTcommUtimeStimeString(std::string_view content,const long ticksPerSec)1311285115cSWilliam A. Kennington III TcommUtimeStime parseTcommUtimeStimeString(std::string_view content,
1321285115cSWilliam A. Kennington III const long ticksPerSec)
1331285115cSWilliam A. Kennington III {
1341285115cSWilliam A. Kennington III TcommUtimeStime ret;
1351285115cSWilliam A. Kennington III ret.tcomm = "";
1361285115cSWilliam A. Kennington III ret.utime = ret.stime = 0;
1371285115cSWilliam A. Kennington III
1381285115cSWilliam A. Kennington III const float invTicksPerSec = 1.0f / static_cast<float>(ticksPerSec);
1391285115cSWilliam A. Kennington III
1401285115cSWilliam A. Kennington III // pCol now points to the first part in content after content is split by
1411285115cSWilliam A. Kennington III // space.
1421285115cSWilliam A. Kennington III // This is not ideal,
1431285115cSWilliam A. Kennington III std::string temp(content);
1441285115cSWilliam A. Kennington III char* pCol = strtok(temp.data(), " ");
1451285115cSWilliam A. Kennington III
1461285115cSWilliam A. Kennington III if (pCol != nullptr)
1471285115cSWilliam A. Kennington III {
1481285115cSWilliam A. Kennington III const int fields[] = {1, 13, 14}; // tcomm, utime, stime
1491285115cSWilliam A. Kennington III int fieldIdx = 0;
1501285115cSWilliam A. Kennington III for (int colIdx = 0; colIdx < 15; ++colIdx)
1511285115cSWilliam A. Kennington III {
1521285115cSWilliam A. Kennington III if (fieldIdx < 3 && colIdx == fields[fieldIdx])
1531285115cSWilliam A. Kennington III {
1541285115cSWilliam A. Kennington III switch (fieldIdx)
1551285115cSWilliam A. Kennington III {
1561285115cSWilliam A. Kennington III case 0:
1571285115cSWilliam A. Kennington III {
1581285115cSWilliam A. Kennington III ret.tcomm = std::string(pCol);
1591285115cSWilliam A. Kennington III break;
1601285115cSWilliam A. Kennington III }
1611285115cSWilliam A. Kennington III case 1:
1621285115cSWilliam A. Kennington III [[fallthrough]];
1631285115cSWilliam A. Kennington III case 2:
1641285115cSWilliam A. Kennington III {
1651285115cSWilliam A. Kennington III int ticks = std::atoi(pCol);
1661285115cSWilliam A. Kennington III float t = static_cast<float>(ticks) * invTicksPerSec;
1671285115cSWilliam A. Kennington III
1681285115cSWilliam A. Kennington III if (fieldIdx == 1)
1691285115cSWilliam A. Kennington III {
1701285115cSWilliam A. Kennington III ret.utime = t;
1711285115cSWilliam A. Kennington III }
1721285115cSWilliam A. Kennington III else if (fieldIdx == 2)
1731285115cSWilliam A. Kennington III {
1741285115cSWilliam A. Kennington III ret.stime = t;
1751285115cSWilliam A. Kennington III }
1761285115cSWilliam A. Kennington III break;
1771285115cSWilliam A. Kennington III }
1781285115cSWilliam A. Kennington III }
1791285115cSWilliam A. Kennington III ++fieldIdx;
1801285115cSWilliam A. Kennington III }
1811285115cSWilliam A. Kennington III pCol = strtok(nullptr, " ");
1821285115cSWilliam A. Kennington III }
1831285115cSWilliam A. Kennington III }
1841285115cSWilliam A. Kennington III
1851285115cSWilliam A. Kennington III if (ticksPerSec <= 0)
1861285115cSWilliam A. Kennington III {
1871285115cSWilliam A. Kennington III log<level::ERR>("ticksPerSec is equal or less than zero");
1881285115cSWilliam A. Kennington III }
1891285115cSWilliam A. Kennington III
1901285115cSWilliam A. Kennington III return ret;
1911285115cSWilliam A. Kennington III }
1921285115cSWilliam A. Kennington III
getTcommUtimeStime(const int pid,const long ticksPerSec)1931285115cSWilliam A. Kennington III TcommUtimeStime getTcommUtimeStime(const int pid, const long ticksPerSec)
1941285115cSWilliam A. Kennington III {
1951285115cSWilliam A. Kennington III const std::string& statPath = "/proc/" + std::to_string(pid) + "/stat";
196b63d6314SMichael Shen return parseTcommUtimeStimeString(readFileThenGrepIntoString(statPath),
1971285115cSWilliam A. Kennington III ticksPerSec);
1981285115cSWilliam A. Kennington III }
1991285115cSWilliam A. Kennington III
2001285115cSWilliam A. Kennington III // Returns true if successfully parsed and false otherwise. If parsing was
2011285115cSWilliam A. Kennington III // successful, value is set accordingly.
2021285115cSWilliam A. Kennington III // Input: "MemAvailable: 1234 kB"
2031285115cSWilliam A. Kennington III // Returns true, value set to 1234
parseMeminfoValue(const std::string_view content,const std::string_view keyword,int & value)2041285115cSWilliam A. Kennington III bool parseMeminfoValue(const std::string_view content,
2051285115cSWilliam A. Kennington III const std::string_view keyword, int& value)
2061285115cSWilliam A. Kennington III {
2071285115cSWilliam A. Kennington III size_t p = content.find(keyword);
2081285115cSWilliam A. Kennington III if (p != std::string::npos)
2091285115cSWilliam A. Kennington III {
2101285115cSWilliam A. Kennington III std::string_view v = content.substr(p + keyword.size());
2111285115cSWilliam A. Kennington III p = v.find("kB");
2121285115cSWilliam A. Kennington III if (p != std::string::npos)
2131285115cSWilliam A. Kennington III {
2141285115cSWilliam A. Kennington III v = v.substr(0, p);
2151285115cSWilliam A. Kennington III value = std::atoi(v.data());
2161285115cSWilliam A. Kennington III return true;
2171285115cSWilliam A. Kennington III }
2181285115cSWilliam A. Kennington III }
2191285115cSWilliam A. Kennington III return false;
2201285115cSWilliam A. Kennington III }
2211285115cSWilliam A. Kennington III
parseProcUptime(const std::string_view content,double & uptime,double & idleProcessTime)2221285115cSWilliam A. Kennington III bool parseProcUptime(const std::string_view content, double& uptime,
2231285115cSWilliam A. Kennington III double& idleProcessTime)
2241285115cSWilliam A. Kennington III {
2251285115cSWilliam A. Kennington III double t0, t1; // Attempts to parse uptime and idleProcessTime
2261285115cSWilliam A. Kennington III int ret = sscanf(content.data(), "%lf %lf", &t0, &t1);
2271285115cSWilliam A. Kennington III if (ret == 2 && std::isfinite(t0) && std::isfinite(t1))
2281285115cSWilliam A. Kennington III {
2291285115cSWilliam A. Kennington III uptime = t0;
2301285115cSWilliam A. Kennington III idleProcessTime = t1;
2311285115cSWilliam A. Kennington III return true;
2321285115cSWilliam A. Kennington III }
2331285115cSWilliam A. Kennington III return false;
2341285115cSWilliam A. Kennington III }
2351285115cSWilliam A. Kennington III
readMem(const uint32_t target,uint32_t & memResult)236b63d6314SMichael Shen bool readMem(const uint32_t target, uint32_t& memResult)
237b63d6314SMichael Shen {
238b63d6314SMichael Shen int fd = open("/dev/mem", O_RDONLY | O_SYNC);
239b63d6314SMichael Shen if (fd < 0)
240b63d6314SMichael Shen {
241b63d6314SMichael Shen return false;
242b63d6314SMichael Shen }
243b63d6314SMichael Shen
244b63d6314SMichael Shen int pageSize = getpagesize();
245b63d6314SMichael Shen uint32_t pageOffset = target & ~static_cast<uint32_t>(pageSize - 1);
246b63d6314SMichael Shen uint32_t offsetInPage = target & static_cast<uint32_t>(pageSize - 1);
247b63d6314SMichael Shen
248*c66ebc35SPatrick Williams void* mapBase =
249*c66ebc35SPatrick Williams mmap(NULL, pageSize * 2, PROT_READ, MAP_SHARED, fd, pageOffset);
250b63d6314SMichael Shen if (mapBase == MAP_FAILED)
251b63d6314SMichael Shen {
252b63d6314SMichael Shen close(fd);
253b63d6314SMichael Shen return false;
254b63d6314SMichael Shen }
255b63d6314SMichael Shen
256b63d6314SMichael Shen char* virtAddr = reinterpret_cast<char*>(mapBase) + offsetInPage;
257b63d6314SMichael Shen memResult = *(reinterpret_cast<uint32_t*>(virtAddr));
258b63d6314SMichael Shen close(fd);
2593d7cd157SMichael Shen munmap(mapBase, pageSize * 2);
260b63d6314SMichael Shen return true;
261b63d6314SMichael Shen }
262b63d6314SMichael Shen
263b63d6314SMichael Shen // clang-format off
264b63d6314SMichael Shen /*
265b63d6314SMichael Shen * power-on
266b63d6314SMichael Shen * counter(start) uptime(start)
267b63d6314SMichael Shen * firmware(Neg) loader(Neg) kernel(always 0) initrd userspace finish
268b63d6314SMichael Shen * |----------------|-------------|-------------------|----------------------|----------------------|
269b63d6314SMichael Shen * |----------------| <--- firmwareTime=firmware-loader
270b63d6314SMichael Shen * |-------------| <--- loaderTime=loader
271b63d6314SMichael Shen * |------------------------------| <--- firmwareTime(Actually is firmware+loader)=counter-uptime \
272b63d6314SMichael Shen * (in this case we can treat this as firmware time \
273b63d6314SMichael Shen * since firmware consumes most of the time)
274b63d6314SMichael Shen * |-------------------| <--- kernelTime=initrd (if initrd present)
275b63d6314SMichael Shen * |------------------------------------------| <--- kernelTime=userspace (if no initrd)
276b63d6314SMichael Shen * |----------------------| <--- initrdTime=userspace-initrd (if initrd present)
277b63d6314SMichael Shen * |----------------------| <--- userspaceTime=finish-userspace
278b63d6314SMichael Shen */
279b63d6314SMichael Shen // clang-format on
getBootTimesMonotonic(BootTimesMonotonic & btm)280b63d6314SMichael Shen bool getBootTimesMonotonic(BootTimesMonotonic& btm)
281b63d6314SMichael Shen {
282b63d6314SMichael Shen // Timestamp name and its offset in the struct.
283b63d6314SMichael Shen std::vector<std::pair<std::string_view, size_t>> timeMap = {
284b63d6314SMichael Shen {"FirmwareTimestampMonotonic",
285b63d6314SMichael Shen offsetof(BootTimesMonotonic, firmwareTime)}, // negative value
286b63d6314SMichael Shen {"LoaderTimestampMonotonic",
287b63d6314SMichael Shen offsetof(BootTimesMonotonic, loaderTime)}, // negative value
288b63d6314SMichael Shen {"InitRDTimestampMonotonic", offsetof(BootTimesMonotonic, initrdTime)},
289b63d6314SMichael Shen {"UserspaceTimestampMonotonic",
290b63d6314SMichael Shen offsetof(BootTimesMonotonic, userspaceTime)},
291b63d6314SMichael Shen {"FinishTimestampMonotonic", offsetof(BootTimesMonotonic, finishTime)}};
292b63d6314SMichael Shen
293b63d6314SMichael Shen auto b = sdbusplus::bus::new_default_system();
294b63d6314SMichael Shen auto m = b.new_method_call("org.freedesktop.systemd1",
295b63d6314SMichael Shen "/org/freedesktop/systemd1",
296b63d6314SMichael Shen "org.freedesktop.DBus.Properties", "GetAll");
297b63d6314SMichael Shen m.append("");
298b63d6314SMichael Shen auto reply = b.call(m);
299b63d6314SMichael Shen std::vector<std::pair<std::string, std::variant<uint64_t>>> timestamps;
300b63d6314SMichael Shen reply.read(timestamps);
301b63d6314SMichael Shen
302b63d6314SMichael Shen // Parse timestamps from dbus result.
303b63d6314SMichael Shen auto btmPtr = reinterpret_cast<char*>(&btm);
304b63d6314SMichael Shen unsigned int recordCnt = 0;
305b63d6314SMichael Shen for (auto& t : timestamps)
306b63d6314SMichael Shen {
307b63d6314SMichael Shen for (auto& tm : timeMap)
308b63d6314SMichael Shen {
309b63d6314SMichael Shen if (tm.first.compare(t.first) == 0)
310b63d6314SMichael Shen {
311b63d6314SMichael Shen auto temp = std::get<uint64_t>(t.second);
312b63d6314SMichael Shen memcpy(btmPtr + tm.second, reinterpret_cast<char*>(&temp),
313b63d6314SMichael Shen sizeof(temp));
314b63d6314SMichael Shen recordCnt++;
315b63d6314SMichael Shen break;
316b63d6314SMichael Shen }
317b63d6314SMichael Shen }
318b63d6314SMichael Shen if (recordCnt == timeMap.size())
319b63d6314SMichael Shen {
320b63d6314SMichael Shen break;
321b63d6314SMichael Shen }
322b63d6314SMichael Shen }
323b63d6314SMichael Shen if (recordCnt != timeMap.size())
324b63d6314SMichael Shen {
325b63d6314SMichael Shen log<level::ERR>("Didn't get desired timestamps");
326b63d6314SMichael Shen return false;
327b63d6314SMichael Shen }
328b63d6314SMichael Shen
329*c66ebc35SPatrick Williams std::string cpuinfo =
330*c66ebc35SPatrick Williams readFileThenGrepIntoString("/proc/cpuinfo", "Hardware");
331b63d6314SMichael Shen // Nuvoton NPCM7XX chip has a counter which starts from power-on.
332b63d6314SMichael Shen if (cpuinfo.find("NPCM7XX") != std::string::npos)
333b63d6314SMichael Shen {
334b63d6314SMichael Shen // Get elapsed seconds from SEC_CNT register
335b63d6314SMichael Shen const uint32_t SEC_CNT_ADDR = 0xf0801068;
336b63d6314SMichael Shen uint32_t memResult = 0;
337b63d6314SMichael Shen if (readMem(SEC_CNT_ADDR, memResult))
338b63d6314SMichael Shen {
339b63d6314SMichael Shen btm.powerOnSecCounterTime = static_cast<uint64_t>(memResult);
340b63d6314SMichael Shen }
341b63d6314SMichael Shen else
342b63d6314SMichael Shen {
343b63d6314SMichael Shen log<level::ERR>("Read memory SEC_CNT_ADDR(0xf0801068) failed");
344b63d6314SMichael Shen return false;
345b63d6314SMichael Shen }
346b63d6314SMichael Shen }
347b63d6314SMichael Shen
348b63d6314SMichael Shen return true;
349b63d6314SMichael Shen }
350b63d6314SMichael Shen
getECCErrorCounts(EccCounts & eccCounts)3514dba220dSWilly Tu bool getECCErrorCounts(EccCounts& eccCounts)
3524dba220dSWilly Tu {
3534dba220dSWilly Tu std::vector<
3544dba220dSWilly Tu std::pair<std::string, std::variant<uint64_t, uint8_t, std::string>>>
3554dba220dSWilly Tu values;
3564dba220dSWilly Tu try
3574dba220dSWilly Tu {
3584dba220dSWilly Tu auto bus = sdbusplus::bus::new_default_system();
3594dba220dSWilly Tu auto m =
3604dba220dSWilly Tu bus.new_method_call("xyz.openbmc_project.memory.ECC",
3614dba220dSWilly Tu "/xyz/openbmc_project/metrics/memory/BmcECC",
3624dba220dSWilly Tu "org.freedesktop.DBus.Properties", "GetAll");
3634dba220dSWilly Tu m.append("xyz.openbmc_project.Memory.MemoryECC");
3644dba220dSWilly Tu auto reply = bus.call(m);
3654dba220dSWilly Tu reply.read(values);
3664dba220dSWilly Tu }
3674dba220dSWilly Tu catch (const sdbusplus::exception::SdBusError& ex)
3684dba220dSWilly Tu {
3694dba220dSWilly Tu return false;
3704dba220dSWilly Tu }
3714dba220dSWilly Tu bool hasCorrectable = false;
3724dba220dSWilly Tu bool hasUncorrectable = false;
3734dba220dSWilly Tu for (const auto& [key, value] : values)
3744dba220dSWilly Tu {
3754dba220dSWilly Tu if (key == "ceCount")
3764dba220dSWilly Tu {
3774dba220dSWilly Tu eccCounts.correctableErrCount =
3784dba220dSWilly Tu static_cast<int32_t>(std::get<uint64_t>(value));
3794dba220dSWilly Tu hasCorrectable = true;
3804dba220dSWilly Tu if (hasUncorrectable)
3814dba220dSWilly Tu {
3824dba220dSWilly Tu return true;
3834dba220dSWilly Tu }
3844dba220dSWilly Tu }
3854dba220dSWilly Tu if (key == "ueCount")
3864dba220dSWilly Tu {
3874dba220dSWilly Tu eccCounts.uncorrectableErrCount =
3884dba220dSWilly Tu static_cast<int32_t>(std::get<uint64_t>(value));
3894dba220dSWilly Tu hasUncorrectable = true;
3904dba220dSWilly Tu if (hasCorrectable)
3914dba220dSWilly Tu {
3924dba220dSWilly Tu return true;
3934dba220dSWilly Tu }
3944dba220dSWilly Tu }
3954dba220dSWilly Tu }
3964dba220dSWilly Tu return false;
3974dba220dSWilly Tu }
3984dba220dSWilly Tu
3991285115cSWilliam A. Kennington III } // namespace metric_blob
400