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 
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 
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 
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 
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.
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 
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.
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 
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
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 
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 
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
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 
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