xref: /openbmc/google-misc/dhcp-done/subprojects/metrics-ipmi-blobs/metric.cpp (revision 4dba220d361271859f824aaa2c7861ceb63ca2dd)
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 "metric.hpp"
161285115cSWilliam A. Kennington III 
177f49370dSWilliam A. Kennington III #include "metricblob.pb.n.h"
181285115cSWilliam A. Kennington III 
191285115cSWilliam A. Kennington III #include "util.hpp"
201285115cSWilliam A. Kennington III 
217f49370dSWilliam A. Kennington III #include <pb_encode.h>
221285115cSWilliam A. Kennington III #include <sys/statvfs.h>
231285115cSWilliam A. Kennington III 
241285115cSWilliam A. Kennington III #include <phosphor-logging/log.hpp>
251285115cSWilliam A. Kennington III 
261285115cSWilliam A. Kennington III #include <cstdint>
271285115cSWilliam A. Kennington III #include <filesystem>
281285115cSWilliam A. Kennington III #include <sstream>
291285115cSWilliam A. Kennington III #include <string>
301285115cSWilliam A. Kennington III #include <string_view>
311285115cSWilliam A. Kennington III 
321285115cSWilliam A. Kennington III namespace metric_blob
331285115cSWilliam A. Kennington III {
341285115cSWilliam A. Kennington III 
351285115cSWilliam A. Kennington III using phosphor::logging::entry;
361285115cSWilliam A. Kennington III using phosphor::logging::log;
371285115cSWilliam A. Kennington III using level = phosphor::logging::level;
381285115cSWilliam A. Kennington III 
391285115cSWilliam A. Kennington III BmcHealthSnapshot::BmcHealthSnapshot() :
401285115cSWilliam A. Kennington III     done(false), stringId(0), ticksPerSec(0)
411285115cSWilliam A. Kennington III {}
421285115cSWilliam A. Kennington III 
437f49370dSWilliam A. Kennington III template <typename T>
447f49370dSWilliam A. Kennington III static constexpr auto pbEncodeStr = [](pb_ostream_t* stream,
457f49370dSWilliam A. Kennington III                                        const pb_field_iter_t* field,
467f49370dSWilliam A. Kennington III                                        void* const* arg) noexcept {
477f49370dSWilliam A. Kennington III     static_assert(sizeof(*std::declval<T>().data()) == sizeof(pb_byte_t));
487f49370dSWilliam A. Kennington III     const auto& s = *reinterpret_cast<const T*>(*arg);
497f49370dSWilliam A. Kennington III     return pb_encode_tag_for_field(stream, field) &&
507f49370dSWilliam A. Kennington III            pb_encode_string(
517f49370dSWilliam A. Kennington III                stream, reinterpret_cast<const pb_byte_t*>(s.data()), s.size());
527f49370dSWilliam A. Kennington III };
537f49370dSWilliam A. Kennington III 
547f49370dSWilliam A. Kennington III template <typename T>
557f49370dSWilliam A. Kennington III static pb_callback_t pbStrEncoder(const T& t) noexcept
567f49370dSWilliam A. Kennington III {
577f49370dSWilliam A. Kennington III     return {{.encode = pbEncodeStr<T>}, const_cast<T*>(&t)};
587f49370dSWilliam A. Kennington III }
597f49370dSWilliam A. Kennington III 
607f49370dSWilliam A. Kennington III template <auto fields, typename T>
617f49370dSWilliam A. Kennington III static constexpr auto pbEncodeSubs = [](pb_ostream_t* stream,
627f49370dSWilliam A. Kennington III                                         const pb_field_iter_t* field,
637f49370dSWilliam A. Kennington III                                         void* const* arg) noexcept {
647f49370dSWilliam A. Kennington III     for (const auto& sub : *reinterpret_cast<const std::vector<T>*>(*arg))
657f49370dSWilliam A. Kennington III     {
667f49370dSWilliam A. Kennington III         if (!pb_encode_tag_for_field(stream, field) ||
677f49370dSWilliam A. Kennington III             !pb_encode_submessage(stream, fields, &sub))
687f49370dSWilliam A. Kennington III         {
697f49370dSWilliam A. Kennington III             return false;
707f49370dSWilliam A. Kennington III         }
717f49370dSWilliam A. Kennington III     }
727f49370dSWilliam A. Kennington III     return true;
737f49370dSWilliam A. Kennington III };
747f49370dSWilliam A. Kennington III 
757f49370dSWilliam A. Kennington III template <auto fields, typename T>
767f49370dSWilliam A. Kennington III static pb_callback_t pbSubsEncoder(const std::vector<T>& t)
777f49370dSWilliam A. Kennington III {
787f49370dSWilliam A. Kennington III     return {{.encode = pbEncodeSubs<fields, T>},
797f49370dSWilliam A. Kennington III             const_cast<std::vector<T>*>(&t)};
807f49370dSWilliam A. Kennington III }
817f49370dSWilliam A. Kennington III 
821285115cSWilliam A. Kennington III struct ProcStatEntry
831285115cSWilliam A. Kennington III {
841285115cSWilliam A. Kennington III     std::string cmdline;
851285115cSWilliam A. Kennington III     std::string tcomm;
861285115cSWilliam A. Kennington III     float utime;
871285115cSWilliam A. Kennington III     float stime;
881285115cSWilliam A. Kennington III 
891285115cSWilliam A. Kennington III     // Processes with the longest utime + stime are ranked first.
901285115cSWilliam A. Kennington III     // Tie breaking is done with cmdline then tcomm.
911285115cSWilliam A. Kennington III     bool operator<(const ProcStatEntry& other) const
921285115cSWilliam A. Kennington III     {
931285115cSWilliam A. Kennington III         const float negTime = -(utime + stime);
941285115cSWilliam A. Kennington III         const float negOtherTime = -(other.utime + other.stime);
951285115cSWilliam A. Kennington III         return std::tie(negTime, cmdline, tcomm) <
961285115cSWilliam A. Kennington III                std::tie(negOtherTime, other.cmdline, other.tcomm);
971285115cSWilliam A. Kennington III     }
981285115cSWilliam A. Kennington III };
991285115cSWilliam A. Kennington III 
1007f49370dSWilliam A. Kennington III static bmcmetrics_metricproto_BmcProcStatMetric getProcStatMetric(
1017f49370dSWilliam A. Kennington III     BmcHealthSnapshot& obj, long ticksPerSec,
1027f49370dSWilliam A. Kennington III     std::vector<bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat>& procs,
1037f49370dSWilliam A. Kennington III     bool& use) noexcept
1041285115cSWilliam A. Kennington III {
1057f49370dSWilliam A. Kennington III     if (ticksPerSec == 0)
1067f49370dSWilliam A. Kennington III     {
1077f49370dSWilliam A. Kennington III         return {};
1087f49370dSWilliam A. Kennington III     }
1091285115cSWilliam A. Kennington III     constexpr std::string_view procPath = "/proc/";
1101285115cSWilliam A. Kennington III 
1111285115cSWilliam A. Kennington III     std::vector<ProcStatEntry> entries;
1121285115cSWilliam A. Kennington III 
1131285115cSWilliam A. Kennington III     for (const auto& procEntry : std::filesystem::directory_iterator(procPath))
1141285115cSWilliam A. Kennington III     {
1151285115cSWilliam A. Kennington III         const std::string& path = procEntry.path();
1161285115cSWilliam A. Kennington III         int pid = -1;
1171285115cSWilliam A. Kennington III         if (isNumericPath(path, pid))
1181285115cSWilliam A. Kennington III         {
1191285115cSWilliam A. Kennington III             ProcStatEntry entry;
1201285115cSWilliam A. Kennington III 
1211285115cSWilliam A. Kennington III             try
1221285115cSWilliam A. Kennington III             {
1231285115cSWilliam A. Kennington III                 entry.cmdline = getCmdLine(pid);
1241285115cSWilliam A. Kennington III                 TcommUtimeStime t = getTcommUtimeStime(pid, ticksPerSec);
1251285115cSWilliam A. Kennington III                 entry.tcomm = t.tcomm;
1261285115cSWilliam A. Kennington III                 entry.utime = t.utime;
1271285115cSWilliam A. Kennington III                 entry.stime = t.stime;
1281285115cSWilliam A. Kennington III 
1291285115cSWilliam A. Kennington III                 entries.push_back(entry);
1301285115cSWilliam A. Kennington III             }
1311285115cSWilliam A. Kennington III             catch (const std::exception& e)
1321285115cSWilliam A. Kennington III             {
1331285115cSWilliam A. Kennington III                 log<level::ERR>("Could not obtain process stats");
1341285115cSWilliam A. Kennington III             }
1351285115cSWilliam A. Kennington III         }
1361285115cSWilliam A. Kennington III     }
1371285115cSWilliam A. Kennington III 
1381285115cSWilliam A. Kennington III     std::sort(entries.begin(), entries.end());
1391285115cSWilliam A. Kennington III 
1401285115cSWilliam A. Kennington III     bool isOthers = false;
1411285115cSWilliam A. Kennington III     ProcStatEntry others;
1421285115cSWilliam A. Kennington III     others.cmdline = "(Others)";
1431285115cSWilliam A. Kennington III     others.utime = others.stime = 0;
1441285115cSWilliam A. Kennington III 
1451285115cSWilliam A. Kennington III     // Only show this many processes and aggregate all remaining ones into
1461285115cSWilliam A. Kennington III     // "others" in order to keep the size of the snapshot reasonably small.
1471285115cSWilliam A. Kennington III     // With 10 process stat entries and 10 FD count entries, the size of the
1481285115cSWilliam A. Kennington III     // snapshot reaches around 1.5KiB. This is non-trivial, and we have to set
1491285115cSWilliam A. Kennington III     // the collection interval long enough so as not to over-stress the IPMI
1501285115cSWilliam A. Kennington III     // interface and the data collection service. The value of 10 is chosen
1511285115cSWilliam A. Kennington III     // empirically, it might be subject to adjustments when the system is
1521285115cSWilliam A. Kennington III     // launched later.
1531285115cSWilliam A. Kennington III     constexpr int topN = 10;
1541285115cSWilliam A. Kennington III 
1551285115cSWilliam A. Kennington III     for (size_t i = 0; i < entries.size(); ++i)
1561285115cSWilliam A. Kennington III     {
1571285115cSWilliam A. Kennington III         if (i >= topN)
1581285115cSWilliam A. Kennington III         {
1591285115cSWilliam A. Kennington III             isOthers = true;
1601285115cSWilliam A. Kennington III         }
1611285115cSWilliam A. Kennington III 
1627f49370dSWilliam A. Kennington III         const ProcStatEntry& entry = entries[i];
1631285115cSWilliam A. Kennington III 
1641285115cSWilliam A. Kennington III         if (isOthers)
1651285115cSWilliam A. Kennington III         {
1661285115cSWilliam A. Kennington III             others.utime += entry.utime;
1671285115cSWilliam A. Kennington III             others.stime += entry.stime;
1681285115cSWilliam A. Kennington III         }
1691285115cSWilliam A. Kennington III         else
1701285115cSWilliam A. Kennington III         {
1711285115cSWilliam A. Kennington III             std::string fullCmdline = entry.cmdline;
1721285115cSWilliam A. Kennington III             if (entry.tcomm.size() > 0)
1731285115cSWilliam A. Kennington III             {
1747f49370dSWilliam A. Kennington III                 fullCmdline += " ";
1757f49370dSWilliam A. Kennington III                 fullCmdline += entry.tcomm;
1761285115cSWilliam A. Kennington III             }
1777f49370dSWilliam A. Kennington III             procs.emplace_back(
1787f49370dSWilliam A. Kennington III                 bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat{
1797f49370dSWilliam A. Kennington III                     .sidx_cmdline = obj.getStringID(fullCmdline),
1807f49370dSWilliam A. Kennington III                     .utime = entry.utime,
1817f49370dSWilliam A. Kennington III                     .stime = entry.stime,
1827f49370dSWilliam A. Kennington III                 });
1831285115cSWilliam A. Kennington III         }
1841285115cSWilliam A. Kennington III     }
1851285115cSWilliam A. Kennington III 
1861285115cSWilliam A. Kennington III     if (isOthers)
1871285115cSWilliam A. Kennington III     {
1887f49370dSWilliam A. Kennington III         procs.emplace_back(bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat{
1897f49370dSWilliam A. Kennington III             .sidx_cmdline = obj.getStringID(others.cmdline),
1907f49370dSWilliam A. Kennington III             .utime = others.utime,
1917f49370dSWilliam A. Kennington III             .stime = others.stime,
1927f49370dSWilliam A. Kennington III 
1937f49370dSWilliam A. Kennington III         });
1941285115cSWilliam A. Kennington III     }
1951285115cSWilliam A. Kennington III 
1967f49370dSWilliam A. Kennington III     use = true;
1977f49370dSWilliam A. Kennington III     return bmcmetrics_metricproto_BmcProcStatMetric{
1987f49370dSWilliam A. Kennington III         .stats = pbSubsEncoder<
1997f49370dSWilliam A. Kennington III             bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat_fields>(procs),
2007f49370dSWilliam A. Kennington III     };
2011285115cSWilliam A. Kennington III }
2021285115cSWilliam A. Kennington III 
2031285115cSWilliam A. Kennington III int getFdCount(int pid)
2041285115cSWilliam A. Kennington III {
2051285115cSWilliam A. Kennington III     const std::string& fdPath = "/proc/" + std::to_string(pid) + "/fd";
2061285115cSWilliam A. Kennington III     return std::distance(std::filesystem::directory_iterator(fdPath),
2071285115cSWilliam A. Kennington III                          std::filesystem::directory_iterator{});
2081285115cSWilliam A. Kennington III }
2091285115cSWilliam A. Kennington III 
2101285115cSWilliam A. Kennington III struct FdStatEntry
2111285115cSWilliam A. Kennington III {
2121285115cSWilliam A. Kennington III     int fdCount;
2131285115cSWilliam A. Kennington III     std::string cmdline;
2141285115cSWilliam A. Kennington III     std::string tcomm;
2151285115cSWilliam A. Kennington III 
2161285115cSWilliam A. Kennington III     // Processes with the largest fdCount goes first.
2171285115cSWilliam A. Kennington III     // Tie-breaking using cmdline then tcomm.
2181285115cSWilliam A. Kennington III     bool operator<(const FdStatEntry& other) const
2191285115cSWilliam A. Kennington III     {
2201285115cSWilliam A. Kennington III         const int negFdCount = -fdCount;
2211285115cSWilliam A. Kennington III         const int negOtherFdCount = -other.fdCount;
2221285115cSWilliam A. Kennington III         return std::tie(negFdCount, cmdline, tcomm) <
2231285115cSWilliam A. Kennington III                std::tie(negOtherFdCount, other.cmdline, other.tcomm);
2241285115cSWilliam A. Kennington III     }
2251285115cSWilliam A. Kennington III };
2261285115cSWilliam A. Kennington III 
2277f49370dSWilliam A. Kennington III static bmcmetrics_metricproto_BmcFdStatMetric getFdStatMetric(
2287f49370dSWilliam A. Kennington III     BmcHealthSnapshot& obj, long ticksPerSec,
2297f49370dSWilliam A. Kennington III     std::vector<bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat>& fds,
2307f49370dSWilliam A. Kennington III     bool& use) noexcept
2311285115cSWilliam A. Kennington III {
2327f49370dSWilliam A. Kennington III     if (ticksPerSec == 0)
2337f49370dSWilliam A. Kennington III     {
2347f49370dSWilliam A. Kennington III         return {};
2357f49370dSWilliam A. Kennington III     }
2361285115cSWilliam A. Kennington III 
2371285115cSWilliam A. Kennington III     // Sort by fd count, no tie-breaking
2381285115cSWilliam A. Kennington III     std::vector<FdStatEntry> entries;
2391285115cSWilliam A. Kennington III 
2401285115cSWilliam A. Kennington III     const std::string_view procPath = "/proc/";
2411285115cSWilliam A. Kennington III     for (const auto& procEntry : std::filesystem::directory_iterator(procPath))
2421285115cSWilliam A. Kennington III     {
2431285115cSWilliam A. Kennington III         const std::string& path = procEntry.path();
2441285115cSWilliam A. Kennington III         int pid = 0;
2451285115cSWilliam A. Kennington III         FdStatEntry entry;
2461285115cSWilliam A. Kennington III         if (isNumericPath(path, pid))
2471285115cSWilliam A. Kennington III         {
2481285115cSWilliam A. Kennington III             try
2491285115cSWilliam A. Kennington III             {
2501285115cSWilliam A. Kennington III                 entry.fdCount = getFdCount(pid);
2511285115cSWilliam A. Kennington III                 TcommUtimeStime t = getTcommUtimeStime(pid, ticksPerSec);
2521285115cSWilliam A. Kennington III                 entry.cmdline = getCmdLine(pid);
2531285115cSWilliam A. Kennington III                 entry.tcomm = t.tcomm;
2541285115cSWilliam A. Kennington III                 entries.push_back(entry);
2551285115cSWilliam A. Kennington III             }
2561285115cSWilliam A. Kennington III             catch (const std::exception& e)
2571285115cSWilliam A. Kennington III             {
2581285115cSWilliam A. Kennington III                 log<level::ERR>("Could not get file descriptor stats");
2591285115cSWilliam A. Kennington III             }
2601285115cSWilliam A. Kennington III         }
2611285115cSWilliam A. Kennington III     }
2621285115cSWilliam A. Kennington III 
2631285115cSWilliam A. Kennington III     std::sort(entries.begin(), entries.end());
2641285115cSWilliam A. Kennington III 
2651285115cSWilliam A. Kennington III     bool isOthers = false;
2661285115cSWilliam A. Kennington III 
2671285115cSWilliam A. Kennington III     // Only report the detailed fd count and cmdline for the top 10 entries,
2681285115cSWilliam A. Kennington III     // and collapse all others into "others".
2691285115cSWilliam A. Kennington III     constexpr int topN = 10;
2701285115cSWilliam A. Kennington III 
2711285115cSWilliam A. Kennington III     FdStatEntry others;
2721285115cSWilliam A. Kennington III     others.cmdline = "(Others)";
2731285115cSWilliam A. Kennington III     others.fdCount = 0;
2741285115cSWilliam A. Kennington III 
2751285115cSWilliam A. Kennington III     for (size_t i = 0; i < entries.size(); ++i)
2761285115cSWilliam A. Kennington III     {
2771285115cSWilliam A. Kennington III         if (i >= topN)
2781285115cSWilliam A. Kennington III         {
2791285115cSWilliam A. Kennington III             isOthers = true;
2801285115cSWilliam A. Kennington III         }
2811285115cSWilliam A. Kennington III 
2821285115cSWilliam A. Kennington III         const FdStatEntry& entry = entries[i];
2831285115cSWilliam A. Kennington III         if (isOthers)
2841285115cSWilliam A. Kennington III         {
2851285115cSWilliam A. Kennington III             others.fdCount += entry.fdCount;
2861285115cSWilliam A. Kennington III         }
2871285115cSWilliam A. Kennington III         else
2881285115cSWilliam A. Kennington III         {
2891285115cSWilliam A. Kennington III             std::string fullCmdline = entry.cmdline;
2901285115cSWilliam A. Kennington III             if (entry.tcomm.size() > 0)
2911285115cSWilliam A. Kennington III             {
2927f49370dSWilliam A. Kennington III                 fullCmdline += " ";
2937f49370dSWilliam A. Kennington III                 fullCmdline += entry.tcomm;
2941285115cSWilliam A. Kennington III             }
2957f49370dSWilliam A. Kennington III             fds.emplace_back(bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat{
2967f49370dSWilliam A. Kennington III                 .sidx_cmdline = obj.getStringID(fullCmdline),
2977f49370dSWilliam A. Kennington III                 .fd_count = entry.fdCount,
2987f49370dSWilliam A. Kennington III             });
2991285115cSWilliam A. Kennington III         }
3001285115cSWilliam A. Kennington III     }
3011285115cSWilliam A. Kennington III 
3021285115cSWilliam A. Kennington III     if (isOthers)
3031285115cSWilliam A. Kennington III     {
3047f49370dSWilliam A. Kennington III         fds.emplace_back(bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat{
3057f49370dSWilliam A. Kennington III             .sidx_cmdline = obj.getStringID(others.cmdline),
3067f49370dSWilliam A. Kennington III             .fd_count = others.fdCount,
3077f49370dSWilliam A. Kennington III         });
3081285115cSWilliam A. Kennington III     }
3091285115cSWilliam A. Kennington III 
3107f49370dSWilliam A. Kennington III     use = true;
3117f49370dSWilliam A. Kennington III     return bmcmetrics_metricproto_BmcFdStatMetric{
3127f49370dSWilliam A. Kennington III         .stats = pbSubsEncoder<
3137f49370dSWilliam A. Kennington III             bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat_fields>(fds),
3147f49370dSWilliam A. Kennington III     };
3157f49370dSWilliam A. Kennington III }
3167f49370dSWilliam A. Kennington III 
317*4dba220dSWilly Tu static bmcmetrics_metricproto_BmcECCMetric getECCMetric(bool& use) noexcept
318*4dba220dSWilly Tu {
319*4dba220dSWilly Tu     EccCounts eccCounts;
320*4dba220dSWilly Tu     use = getECCErrorCounts(eccCounts);
321*4dba220dSWilly Tu     if (!use)
322*4dba220dSWilly Tu     {
323*4dba220dSWilly Tu         return {};
324*4dba220dSWilly Tu     }
325*4dba220dSWilly Tu     return bmcmetrics_metricproto_BmcECCMetric{
326*4dba220dSWilly Tu         .correctable_error_count = eccCounts.correctableErrCount,
327*4dba220dSWilly Tu         .uncorrectable_error_count = eccCounts.uncorrectableErrCount,
328*4dba220dSWilly Tu     };
329*4dba220dSWilly Tu }
330*4dba220dSWilly Tu 
3317f49370dSWilliam A. Kennington III static bmcmetrics_metricproto_BmcMemoryMetric getMemMetric() noexcept
3327f49370dSWilliam A. Kennington III {
3337f49370dSWilliam A. Kennington III     bmcmetrics_metricproto_BmcMemoryMetric ret = {};
3347f49370dSWilliam A. Kennington III     auto data = readFileThenGrepIntoString("/proc/meminfo");
3357f49370dSWilliam A. Kennington III     int value;
3367f49370dSWilliam A. Kennington III     if (parseMeminfoValue(data, "MemAvailable:", value))
3377f49370dSWilliam A. Kennington III     {
3387f49370dSWilliam A. Kennington III         ret.mem_available = value;
3397f49370dSWilliam A. Kennington III     }
3407f49370dSWilliam A. Kennington III     if (parseMeminfoValue(data, "Slab:", value))
3417f49370dSWilliam A. Kennington III     {
3427f49370dSWilliam A. Kennington III         ret.slab = value;
3437f49370dSWilliam A. Kennington III     }
3447f49370dSWilliam A. Kennington III 
3457f49370dSWilliam A. Kennington III     if (parseMeminfoValue(data, "KernelStack:", value))
3467f49370dSWilliam A. Kennington III     {
3477f49370dSWilliam A. Kennington III         ret.kernel_stack = value;
3487f49370dSWilliam A. Kennington III     }
3491285115cSWilliam A. Kennington III     return ret;
3501285115cSWilliam A. Kennington III }
3511285115cSWilliam A. Kennington III 
3527f49370dSWilliam A. Kennington III static bmcmetrics_metricproto_BmcUptimeMetric
3537f49370dSWilliam A. Kennington III     getUptimeMetric(bool& use) noexcept
3541285115cSWilliam A. Kennington III {
3557f49370dSWilliam A. Kennington III     bmcmetrics_metricproto_BmcUptimeMetric ret = {};
3561285115cSWilliam A. Kennington III 
357b63d6314SMichael Shen     double uptime = 0;
3587f49370dSWilliam A. Kennington III     {
3597f49370dSWilliam A. Kennington III         auto data = readFileThenGrepIntoString("/proc/uptime");
360b63d6314SMichael Shen         double idleProcessTime = 0;
3617f49370dSWilliam A. Kennington III         if (!parseProcUptime(data, uptime, idleProcessTime))
362b63d6314SMichael Shen         {
363b63d6314SMichael Shen             log<level::ERR>("Error parsing /proc/uptime");
3647f49370dSWilliam A. Kennington III             return ret;
365b63d6314SMichael Shen         }
3667f49370dSWilliam A. Kennington III         ret.uptime = uptime;
3677f49370dSWilliam A. Kennington III         ret.idle_process_time = idleProcessTime;
3681285115cSWilliam A. Kennington III     }
3691285115cSWilliam A. Kennington III 
3707f49370dSWilliam A. Kennington III     BootTimesMonotonic btm;
3717f49370dSWilliam A. Kennington III     if (!getBootTimesMonotonic(btm))
3727f49370dSWilliam A. Kennington III     {
3737f49370dSWilliam A. Kennington III         log<level::ERR>("Could not get boot time");
3747f49370dSWilliam A. Kennington III         return ret;
3757f49370dSWilliam A. Kennington III     }
3767f49370dSWilliam A. Kennington III     if (btm.firmwareTime == 0 && btm.powerOnSecCounterTime != 0)
3777f49370dSWilliam A. Kennington III     {
3787f49370dSWilliam A. Kennington III         ret.firmware_boot_time_sec =
3797f49370dSWilliam A. Kennington III             static_cast<double>(btm.powerOnSecCounterTime) - uptime;
3807f49370dSWilliam A. Kennington III     }
3817f49370dSWilliam A. Kennington III     else
3827f49370dSWilliam A. Kennington III     {
3837f49370dSWilliam A. Kennington III         ret.firmware_boot_time_sec =
3847f49370dSWilliam A. Kennington III             static_cast<double>(btm.firmwareTime - btm.loaderTime) / 1e6;
3857f49370dSWilliam A. Kennington III     }
3867f49370dSWilliam A. Kennington III     ret.loader_boot_time_sec = static_cast<double>(btm.loaderTime) / 1e6;
3877f49370dSWilliam A. Kennington III     if (btm.initrdTime != 0)
3887f49370dSWilliam A. Kennington III     {
3897f49370dSWilliam A. Kennington III         ret.kernel_boot_time_sec = static_cast<double>(btm.initrdTime) / 1e6;
3907f49370dSWilliam A. Kennington III         ret.initrd_boot_time_sec =
3917f49370dSWilliam A. Kennington III             static_cast<double>(btm.userspaceTime - btm.initrdTime) / 1e6;
3927f49370dSWilliam A. Kennington III         ret.userspace_boot_time_sec =
3937f49370dSWilliam A. Kennington III             static_cast<double>(btm.finishTime - btm.userspaceTime) / 1e6;
3947f49370dSWilliam A. Kennington III     }
3957f49370dSWilliam A. Kennington III     else
3967f49370dSWilliam A. Kennington III     {
3977f49370dSWilliam A. Kennington III         ret.kernel_boot_time_sec = static_cast<double>(btm.userspaceTime) / 1e6;
3987f49370dSWilliam A. Kennington III         ret.initrd_boot_time_sec = 0;
3997f49370dSWilliam A. Kennington III         ret.userspace_boot_time_sec =
4007f49370dSWilliam A. Kennington III             static_cast<double>(btm.finishTime - btm.userspaceTime) / 1e6;
4017f49370dSWilliam A. Kennington III     }
4027f49370dSWilliam A. Kennington III 
4037f49370dSWilliam A. Kennington III     use = true;
4047f49370dSWilliam A. Kennington III     return ret;
4057f49370dSWilliam A. Kennington III }
4067f49370dSWilliam A. Kennington III 
4077f49370dSWilliam A. Kennington III static bmcmetrics_metricproto_BmcDiskSpaceMetric
4087f49370dSWilliam A. Kennington III     getStorageMetric(bool& use) noexcept
4097f49370dSWilliam A. Kennington III {
4107f49370dSWilliam A. Kennington III     bmcmetrics_metricproto_BmcDiskSpaceMetric ret = {};
4111285115cSWilliam A. Kennington III     struct statvfs fiData;
4127f49370dSWilliam A. Kennington III     if (statvfs("/", &fiData) < 0)
4131285115cSWilliam A. Kennington III     {
4141285115cSWilliam A. Kennington III         log<level::ERR>("Could not call statvfs");
4151285115cSWilliam A. Kennington III     }
4161285115cSWilliam A. Kennington III     else
4171285115cSWilliam A. Kennington III     {
4187f49370dSWilliam A. Kennington III         ret.rwfs_kib_available = (fiData.f_bsize * fiData.f_bfree) / 1024;
4197f49370dSWilliam A. Kennington III         use = true;
4207f49370dSWilliam A. Kennington III     }
4217f49370dSWilliam A. Kennington III     return ret;
4221285115cSWilliam A. Kennington III }
4231285115cSWilliam A. Kennington III 
4247f49370dSWilliam A. Kennington III void BmcHealthSnapshot::doWork()
4257f49370dSWilliam A. Kennington III {
4261285115cSWilliam A. Kennington III     // The next metrics require a sane ticks_per_sec value, typically 100 on
4271285115cSWilliam A. Kennington III     // the BMC. In the very rare circumstance when it's 0, exit early and return
4281285115cSWilliam A. Kennington III     // a partially complete snapshot (no process).
4291285115cSWilliam A. Kennington III     ticksPerSec = getTicksPerSec();
4301285115cSWilliam A. Kennington III 
4317f49370dSWilliam A. Kennington III     static constexpr auto stcb = [](pb_ostream_t* stream,
4327f49370dSWilliam A. Kennington III                                     const pb_field_t* field,
4337f49370dSWilliam A. Kennington III                                     void* const* arg) noexcept {
4347f49370dSWilliam A. Kennington III         auto& self = *reinterpret_cast<BmcHealthSnapshot*>(*arg);
4357f49370dSWilliam A. Kennington III         std::vector<std::string_view> strs(self.stringTable.size());
4367f49370dSWilliam A. Kennington III         for (const auto& [str, i] : self.stringTable)
4371285115cSWilliam A. Kennington III         {
4387f49370dSWilliam A. Kennington III             strs[i] = str;
4397f49370dSWilliam A. Kennington III         }
4407f49370dSWilliam A. Kennington III         for (auto& str : strs)
4417f49370dSWilliam A. Kennington III         {
4427f49370dSWilliam A. Kennington III             bmcmetrics_metricproto_BmcStringTable_StringEntry msg = {
4437f49370dSWilliam A. Kennington III                 .value = pbStrEncoder(str),
4447f49370dSWilliam A. Kennington III             };
4457f49370dSWilliam A. Kennington III             if (!pb_encode_tag_for_field(stream, field) ||
4467f49370dSWilliam A. Kennington III                 !pb_encode_submessage(
4477f49370dSWilliam A. Kennington III                     stream,
4487f49370dSWilliam A. Kennington III                     bmcmetrics_metricproto_BmcStringTable_StringEntry_fields,
4497f49370dSWilliam A. Kennington III                     &msg))
4507f49370dSWilliam A. Kennington III             {
4517f49370dSWilliam A. Kennington III                 return false;
4527f49370dSWilliam A. Kennington III             }
4537f49370dSWilliam A. Kennington III         }
4547f49370dSWilliam A. Kennington III         return true;
4557f49370dSWilliam A. Kennington III     };
4567f49370dSWilliam A. Kennington III     std::vector<bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat> procs;
4577f49370dSWilliam A. Kennington III     std::vector<bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat> fds;
4587f49370dSWilliam A. Kennington III     bmcmetrics_metricproto_BmcMetricSnapshot snapshot = {
4597f49370dSWilliam A. Kennington III         .has_string_table = true,
4607f49370dSWilliam A. Kennington III         .string_table =
4617f49370dSWilliam A. Kennington III             {
4627f49370dSWilliam A. Kennington III                 .entries = {{.encode = stcb}, this},
4637f49370dSWilliam A. Kennington III             },
4647f49370dSWilliam A. Kennington III         .has_memory_metric = true,
4657f49370dSWilliam A. Kennington III         .memory_metric = getMemMetric(),
4667f49370dSWilliam A. Kennington III         .has_uptime_metric = false,
4677f49370dSWilliam A. Kennington III         .uptime_metric = getUptimeMetric(snapshot.has_uptime_metric),
4687f49370dSWilliam A. Kennington III         .has_storage_space_metric = false,
4697f49370dSWilliam A. Kennington III         .storage_space_metric =
4707f49370dSWilliam A. Kennington III             getStorageMetric(snapshot.has_storage_space_metric),
4717f49370dSWilliam A. Kennington III         .has_procstat_metric = false,
4727f49370dSWilliam A. Kennington III         .procstat_metric = getProcStatMetric(*this, ticksPerSec, procs,
4737f49370dSWilliam A. Kennington III                                              snapshot.has_procstat_metric),
4747f49370dSWilliam A. Kennington III         .has_fdstat_metric = false,
4757f49370dSWilliam A. Kennington III         .fdstat_metric = getFdStatMetric(*this, ticksPerSec, fds,
4767f49370dSWilliam A. Kennington III                                          snapshot.has_fdstat_metric),
477*4dba220dSWilly Tu         .has_ecc_metric = false,
478*4dba220dSWilly Tu         .ecc_metric = getECCMetric(snapshot.has_ecc_metric),
4797f49370dSWilliam A. Kennington III     };
4807f49370dSWilliam A. Kennington III     pb_ostream_t nost = {};
4817f49370dSWilliam A. Kennington III     if (!pb_encode(&nost, bmcmetrics_metricproto_BmcMetricSnapshot_fields,
4827f49370dSWilliam A. Kennington III                    &snapshot))
4837f49370dSWilliam A. Kennington III     {
4847f49370dSWilliam A. Kennington III         auto msg = std::format("Getting pb size: {}", PB_GET_ERROR(&nost));
4857f49370dSWilliam A. Kennington III         log<level::ERR>(msg.c_str());
4861285115cSWilliam A. Kennington III         return;
4871285115cSWilliam A. Kennington III     }
4887f49370dSWilliam A. Kennington III     pbDump.resize(nost.bytes_written);
4897f49370dSWilliam A. Kennington III     auto ost = pb_ostream_from_buffer(
4907f49370dSWilliam A. Kennington III         reinterpret_cast<pb_byte_t*>(pbDump.data()), pbDump.size());
4917f49370dSWilliam A. Kennington III     if (!pb_encode(&ost, bmcmetrics_metricproto_BmcMetricSnapshot_fields,
4927f49370dSWilliam A. Kennington III                    &snapshot))
4931285115cSWilliam A. Kennington III     {
4947f49370dSWilliam A. Kennington III         auto msg = std::format("Writing pb msg: {}", PB_GET_ERROR(&ost));
4957f49370dSWilliam A. Kennington III         log<level::ERR>(msg.c_str());
4967f49370dSWilliam A. Kennington III         return;
4971285115cSWilliam A. Kennington III     }
4981285115cSWilliam A. Kennington III     done = true;
4991285115cSWilliam A. Kennington III }
5001285115cSWilliam A. Kennington III 
5011285115cSWilliam A. Kennington III // BmcBlobSessionStat (9) but passing meta as reference instead of pointer,
5021285115cSWilliam A. Kennington III // since the metadata must not be null at this point.
5031285115cSWilliam A. Kennington III bool BmcHealthSnapshot::stat(blobs::BlobMeta& meta)
5041285115cSWilliam A. Kennington III {
5051285115cSWilliam A. Kennington III     if (!done)
5061285115cSWilliam A. Kennington III     {
5071285115cSWilliam A. Kennington III         // Bits 8~15 are blob-specific state flags.
5081285115cSWilliam A. Kennington III         // For this blob, bit 8 is set when metric collection is still in
5091285115cSWilliam A. Kennington III         // progress.
5101285115cSWilliam A. Kennington III         meta.blobState |= (1 << 8);
5111285115cSWilliam A. Kennington III     }
5121285115cSWilliam A. Kennington III     else
5131285115cSWilliam A. Kennington III     {
5141285115cSWilliam A. Kennington III         meta.blobState = 0;
5151285115cSWilliam A. Kennington III         meta.blobState = blobs::StateFlags::open_read;
5161285115cSWilliam A. Kennington III         meta.size = pbDump.size();
5171285115cSWilliam A. Kennington III     }
5181285115cSWilliam A. Kennington III     return true;
5191285115cSWilliam A. Kennington III }
5201285115cSWilliam A. Kennington III 
5211285115cSWilliam A. Kennington III std::string_view BmcHealthSnapshot::read(uint32_t offset,
5221285115cSWilliam A. Kennington III                                          uint32_t requestedSize)
5231285115cSWilliam A. Kennington III {
5241285115cSWilliam A. Kennington III     uint32_t size = static_cast<uint32_t>(pbDump.size());
5251285115cSWilliam A. Kennington III     if (offset >= size)
5261285115cSWilliam A. Kennington III     {
5271285115cSWilliam A. Kennington III         return {};
5281285115cSWilliam A. Kennington III     }
5291285115cSWilliam A. Kennington III     return std::string_view(pbDump.data() + offset,
5301285115cSWilliam A. Kennington III                             std::min(requestedSize, size - offset));
5311285115cSWilliam A. Kennington III }
5321285115cSWilliam A. Kennington III 
5331285115cSWilliam A. Kennington III int BmcHealthSnapshot::getStringID(const std::string_view s)
5341285115cSWilliam A. Kennington III {
5351285115cSWilliam A. Kennington III     int ret = 0;
5361285115cSWilliam A. Kennington III     auto itr = stringTable.find(s.data());
5371285115cSWilliam A. Kennington III     if (itr == stringTable.end())
5381285115cSWilliam A. Kennington III     {
5391285115cSWilliam A. Kennington III         stringTable[s.data()] = stringId;
5401285115cSWilliam A. Kennington III         ret = stringId;
5411285115cSWilliam A. Kennington III         ++stringId;
5421285115cSWilliam A. Kennington III     }
5431285115cSWilliam A. Kennington III     else
5441285115cSWilliam A. Kennington III     {
5451285115cSWilliam A. Kennington III         ret = itr->second;
5461285115cSWilliam A. Kennington III     }
5471285115cSWilliam A. Kennington III     return ret;
5481285115cSWilliam A. Kennington III }
5491285115cSWilliam A. Kennington III 
5501285115cSWilliam A. Kennington III } // namespace metric_blob
551