xref: /openbmc/google-misc/dhcp-done/subprojects/metrics-ipmi-blobs/util.cpp (revision b63d6314d059140f1ed23456d798544ac8e30d45)
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 
17*b63d6314SMichael Shen #include <fcntl.h>
18*b63d6314SMichael Shen #include <sys/mman.h>
19*b63d6314SMichael 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>
23*b63d6314SMichael Shen #include <sdbusplus/bus.hpp>
24*b63d6314SMichael 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>
32*b63d6314SMichael Shen #include <unordered_map>
33*b63d6314SMichael Shen #include <utility>
34*b63d6314SMichael Shen #include <variant>
35*b63d6314SMichael 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 
57*b63d6314SMichael Shen std::string readFileThenGrepIntoString(const std::string_view fileName,
58*b63d6314SMichael 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);
66*b63d6314SMichael Shen         if (line.find(grepStr) != std::string::npos)
67*b63d6314SMichael Shen         {
681285115cSWilliam A. Kennington III             ss << line;
69*b63d6314SMichael 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 {
1141285115cSWilliam A. Kennington III     const std::string& cmdlinePath =
1151285115cSWilliam A. Kennington III         "/proc/" + std::to_string(pid) + "/cmdline";
1161285115cSWilliam A. Kennington III 
117*b63d6314SMichael 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";
196*b63d6314SMichael 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 
236*b63d6314SMichael Shen bool readMem(const uint32_t target, uint32_t& memResult)
237*b63d6314SMichael Shen {
238*b63d6314SMichael Shen     int fd = open("/dev/mem", O_RDONLY | O_SYNC);
239*b63d6314SMichael Shen     if (fd < 0)
240*b63d6314SMichael Shen     {
241*b63d6314SMichael Shen         return false;
242*b63d6314SMichael Shen     }
243*b63d6314SMichael Shen 
244*b63d6314SMichael Shen     int pageSize = getpagesize();
245*b63d6314SMichael Shen     uint32_t pageOffset = target & ~static_cast<uint32_t>(pageSize - 1);
246*b63d6314SMichael Shen     uint32_t offsetInPage = target & static_cast<uint32_t>(pageSize - 1);
247*b63d6314SMichael Shen 
248*b63d6314SMichael Shen     void* mapBase =
249*b63d6314SMichael Shen         mmap(NULL, pageSize * 2, PROT_READ, MAP_SHARED, fd, pageOffset);
250*b63d6314SMichael Shen     if (mapBase == MAP_FAILED)
251*b63d6314SMichael Shen     {
252*b63d6314SMichael Shen         close(fd);
253*b63d6314SMichael Shen         return false;
254*b63d6314SMichael Shen     }
255*b63d6314SMichael Shen 
256*b63d6314SMichael Shen     char* virtAddr = reinterpret_cast<char*>(mapBase) + offsetInPage;
257*b63d6314SMichael Shen     memResult = *(reinterpret_cast<uint32_t*>(virtAddr));
258*b63d6314SMichael Shen     close(fd);
259*b63d6314SMichael Shen     return true;
260*b63d6314SMichael Shen }
261*b63d6314SMichael Shen 
262*b63d6314SMichael Shen // clang-format off
263*b63d6314SMichael Shen /*
264*b63d6314SMichael Shen  *  power-on
265*b63d6314SMichael Shen  *  counter(start)                 uptime(start)
266*b63d6314SMichael Shen  *  firmware(Neg)    loader(Neg)   kernel(always 0)    initrd                 userspace              finish
267*b63d6314SMichael Shen  *  |----------------|-------------|-------------------|----------------------|----------------------|
268*b63d6314SMichael Shen  *  |----------------| <--- firmwareTime=firmware-loader
269*b63d6314SMichael Shen  *                   |-------------| <--- loaderTime=loader
270*b63d6314SMichael Shen  *  |------------------------------| <--- firmwareTime(Actually is firmware+loader)=counter-uptime \
271*b63d6314SMichael Shen  *                                        (in this case we can treat this as firmware time \
272*b63d6314SMichael Shen  *                                         since firmware consumes most of the time)
273*b63d6314SMichael Shen  *                                 |-------------------| <--- kernelTime=initrd (if initrd present)
274*b63d6314SMichael Shen  *                                 |------------------------------------------| <--- kernelTime=userspace (if no initrd)
275*b63d6314SMichael Shen  *                                                     |----------------------| <--- initrdTime=userspace-initrd (if initrd present)
276*b63d6314SMichael Shen  *                                                                            |----------------------| <--- userspaceTime=finish-userspace
277*b63d6314SMichael Shen  */
278*b63d6314SMichael Shen // clang-format on
279*b63d6314SMichael Shen bool getBootTimesMonotonic(BootTimesMonotonic& btm)
280*b63d6314SMichael Shen {
281*b63d6314SMichael Shen     // Timestamp name and its offset in the struct.
282*b63d6314SMichael Shen     std::vector<std::pair<std::string_view, size_t>> timeMap = {
283*b63d6314SMichael Shen         {"FirmwareTimestampMonotonic",
284*b63d6314SMichael Shen          offsetof(BootTimesMonotonic, firmwareTime)}, // negative value
285*b63d6314SMichael Shen         {"LoaderTimestampMonotonic",
286*b63d6314SMichael Shen          offsetof(BootTimesMonotonic, loaderTime)}, // negative value
287*b63d6314SMichael Shen         {"InitRDTimestampMonotonic", offsetof(BootTimesMonotonic, initrdTime)},
288*b63d6314SMichael Shen         {"UserspaceTimestampMonotonic",
289*b63d6314SMichael Shen          offsetof(BootTimesMonotonic, userspaceTime)},
290*b63d6314SMichael Shen         {"FinishTimestampMonotonic", offsetof(BootTimesMonotonic, finishTime)}};
291*b63d6314SMichael Shen 
292*b63d6314SMichael Shen     auto b = sdbusplus::bus::new_default_system();
293*b63d6314SMichael Shen     auto m = b.new_method_call("org.freedesktop.systemd1",
294*b63d6314SMichael Shen                                "/org/freedesktop/systemd1",
295*b63d6314SMichael Shen                                "org.freedesktop.DBus.Properties", "GetAll");
296*b63d6314SMichael Shen     m.append("");
297*b63d6314SMichael Shen     auto reply = b.call(m);
298*b63d6314SMichael Shen     std::vector<std::pair<std::string, std::variant<uint64_t>>> timestamps;
299*b63d6314SMichael Shen     reply.read(timestamps);
300*b63d6314SMichael Shen 
301*b63d6314SMichael Shen     // Parse timestamps from dbus result.
302*b63d6314SMichael Shen     auto btmPtr = reinterpret_cast<char*>(&btm);
303*b63d6314SMichael Shen     unsigned int recordCnt = 0;
304*b63d6314SMichael Shen     for (auto& t : timestamps)
305*b63d6314SMichael Shen     {
306*b63d6314SMichael Shen         for (auto& tm : timeMap)
307*b63d6314SMichael Shen         {
308*b63d6314SMichael Shen             if (tm.first.compare(t.first) == 0)
309*b63d6314SMichael Shen             {
310*b63d6314SMichael Shen                 auto temp = std::get<uint64_t>(t.second);
311*b63d6314SMichael Shen                 memcpy(btmPtr + tm.second, reinterpret_cast<char*>(&temp),
312*b63d6314SMichael Shen                        sizeof(temp));
313*b63d6314SMichael Shen                 recordCnt++;
314*b63d6314SMichael Shen                 break;
315*b63d6314SMichael Shen             }
316*b63d6314SMichael Shen         }
317*b63d6314SMichael Shen         if (recordCnt == timeMap.size())
318*b63d6314SMichael Shen         {
319*b63d6314SMichael Shen             break;
320*b63d6314SMichael Shen         }
321*b63d6314SMichael Shen     }
322*b63d6314SMichael Shen     if (recordCnt != timeMap.size())
323*b63d6314SMichael Shen     {
324*b63d6314SMichael Shen         log<level::ERR>("Didn't get desired timestamps");
325*b63d6314SMichael Shen         return false;
326*b63d6314SMichael Shen     }
327*b63d6314SMichael Shen 
328*b63d6314SMichael Shen     std::string cpuinfo =
329*b63d6314SMichael Shen         readFileThenGrepIntoString("/proc/cpuinfo", "Hardware");
330*b63d6314SMichael Shen     // Nuvoton NPCM7XX chip has a counter which starts from power-on.
331*b63d6314SMichael Shen     if (cpuinfo.find("NPCM7XX") != std::string::npos)
332*b63d6314SMichael Shen     {
333*b63d6314SMichael Shen         // Get elapsed seconds from SEC_CNT register
334*b63d6314SMichael Shen         const uint32_t SEC_CNT_ADDR = 0xf0801068;
335*b63d6314SMichael Shen         uint32_t memResult = 0;
336*b63d6314SMichael Shen         if (readMem(SEC_CNT_ADDR, memResult))
337*b63d6314SMichael Shen         {
338*b63d6314SMichael Shen             btm.powerOnSecCounterTime = static_cast<uint64_t>(memResult);
339*b63d6314SMichael Shen         }
340*b63d6314SMichael Shen         else
341*b63d6314SMichael Shen         {
342*b63d6314SMichael Shen             log<level::ERR>("Read memory SEC_CNT_ADDR(0xf0801068) failed");
343*b63d6314SMichael Shen             return false;
344*b63d6314SMichael Shen         }
345*b63d6314SMichael Shen     }
346*b63d6314SMichael Shen 
347*b63d6314SMichael Shen     return true;
348*b63d6314SMichael Shen }
349*b63d6314SMichael Shen 
3501285115cSWilliam A. Kennington III } // namespace metric_blob