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>
44*c66ebc35SPatrick Williams static constexpr auto pbEncodeStr =
45*c66ebc35SPatrick Williams     [](pb_ostream_t* stream, 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) &&
50*c66ebc35SPatrick Williams                pb_encode_string(stream,
51*c66ebc35SPatrick Williams                                 reinterpret_cast<const pb_byte_t*>(s.data()),
52*c66ebc35SPatrick Williams                                 s.size());
537f49370dSWilliam A. Kennington III     };
547f49370dSWilliam A. Kennington III 
557f49370dSWilliam A. Kennington III template <typename T>
567f49370dSWilliam A. Kennington III static pb_callback_t pbStrEncoder(const T& t) noexcept
577f49370dSWilliam A. Kennington III {
587f49370dSWilliam A. Kennington III     return {{.encode = pbEncodeStr<T>}, const_cast<T*>(&t)};
597f49370dSWilliam A. Kennington III }
607f49370dSWilliam A. Kennington III 
617f49370dSWilliam A. Kennington III template <auto fields, typename T>
62*c66ebc35SPatrick Williams static constexpr auto pbEncodeSubs =
63*c66ebc35SPatrick Williams     [](pb_ostream_t* stream, const pb_field_iter_t* field,
647f49370dSWilliam A. Kennington III        void* const* arg) noexcept {
657f49370dSWilliam A. Kennington III         for (const auto& sub : *reinterpret_cast<const std::vector<T>*>(*arg))
667f49370dSWilliam A. Kennington III         {
677f49370dSWilliam A. Kennington III             if (!pb_encode_tag_for_field(stream, field) ||
687f49370dSWilliam A. Kennington III                 !pb_encode_submessage(stream, fields, &sub))
697f49370dSWilliam A. Kennington III             {
707f49370dSWilliam A. Kennington III                 return false;
717f49370dSWilliam A. Kennington III             }
727f49370dSWilliam A. Kennington III         }
737f49370dSWilliam A. Kennington III         return true;
747f49370dSWilliam A. Kennington III     };
757f49370dSWilliam A. Kennington III 
767f49370dSWilliam A. Kennington III template <auto fields, typename T>
777f49370dSWilliam A. Kennington III static pb_callback_t pbSubsEncoder(const std::vector<T>& t)
787f49370dSWilliam A. Kennington III {
797f49370dSWilliam A. Kennington III     return {{.encode = pbEncodeSubs<fields, T>},
807f49370dSWilliam A. Kennington III             const_cast<std::vector<T>*>(&t)};
817f49370dSWilliam A. Kennington III }
827f49370dSWilliam A. Kennington III 
831285115cSWilliam A. Kennington III struct ProcStatEntry
841285115cSWilliam A. Kennington III {
851285115cSWilliam A. Kennington III     std::string cmdline;
861285115cSWilliam A. Kennington III     std::string tcomm;
871285115cSWilliam A. Kennington III     float utime;
881285115cSWilliam A. Kennington III     float stime;
891285115cSWilliam A. Kennington III 
901285115cSWilliam A. Kennington III     // Processes with the longest utime + stime are ranked first.
911285115cSWilliam A. Kennington III     // Tie breaking is done with cmdline then tcomm.
921285115cSWilliam A. Kennington III     bool operator<(const ProcStatEntry& other) const
931285115cSWilliam A. Kennington III     {
941285115cSWilliam A. Kennington III         const float negTime = -(utime + stime);
951285115cSWilliam A. Kennington III         const float negOtherTime = -(other.utime + other.stime);
961285115cSWilliam A. Kennington III         return std::tie(negTime, cmdline, tcomm) <
971285115cSWilliam A. Kennington III                std::tie(negOtherTime, other.cmdline, other.tcomm);
981285115cSWilliam A. Kennington III     }
991285115cSWilliam A. Kennington III };
1001285115cSWilliam A. Kennington III 
1017f49370dSWilliam A. Kennington III static bmcmetrics_metricproto_BmcProcStatMetric getProcStatMetric(
1027f49370dSWilliam A. Kennington III     BmcHealthSnapshot& obj, long ticksPerSec,
1037f49370dSWilliam A. Kennington III     std::vector<bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat>& procs,
1047f49370dSWilliam A. Kennington III     bool& use) noexcept
1051285115cSWilliam A. Kennington III {
1067f49370dSWilliam A. Kennington III     if (ticksPerSec == 0)
1077f49370dSWilliam A. Kennington III     {
1087f49370dSWilliam A. Kennington III         return {};
1097f49370dSWilliam A. Kennington III     }
1101285115cSWilliam A. Kennington III     constexpr std::string_view procPath = "/proc/";
1111285115cSWilliam A. Kennington III 
1121285115cSWilliam A. Kennington III     std::vector<ProcStatEntry> entries;
1131285115cSWilliam A. Kennington III 
1141285115cSWilliam A. Kennington III     for (const auto& procEntry : std::filesystem::directory_iterator(procPath))
1151285115cSWilliam A. Kennington III     {
1161285115cSWilliam A. Kennington III         const std::string& path = procEntry.path();
1171285115cSWilliam A. Kennington III         int pid = -1;
1181285115cSWilliam A. Kennington III         if (isNumericPath(path, pid))
1191285115cSWilliam A. Kennington III         {
1201285115cSWilliam A. Kennington III             ProcStatEntry entry;
1211285115cSWilliam A. Kennington III 
1221285115cSWilliam A. Kennington III             try
1231285115cSWilliam A. Kennington III             {
1241285115cSWilliam A. Kennington III                 entry.cmdline = getCmdLine(pid);
1251285115cSWilliam A. Kennington III                 TcommUtimeStime t = getTcommUtimeStime(pid, ticksPerSec);
1261285115cSWilliam A. Kennington III                 entry.tcomm = t.tcomm;
1271285115cSWilliam A. Kennington III                 entry.utime = t.utime;
1281285115cSWilliam A. Kennington III                 entry.stime = t.stime;
1291285115cSWilliam A. Kennington III 
1301285115cSWilliam A. Kennington III                 entries.push_back(entry);
1311285115cSWilliam A. Kennington III             }
1321285115cSWilliam A. Kennington III             catch (const std::exception& e)
1331285115cSWilliam A. Kennington III             {
1341285115cSWilliam A. Kennington III                 log<level::ERR>("Could not obtain process stats");
1351285115cSWilliam A. Kennington III             }
1361285115cSWilliam A. Kennington III         }
1371285115cSWilliam A. Kennington III     }
1381285115cSWilliam A. Kennington III 
1391285115cSWilliam A. Kennington III     std::sort(entries.begin(), entries.end());
1401285115cSWilliam A. Kennington III 
1411285115cSWilliam A. Kennington III     bool isOthers = false;
1421285115cSWilliam A. Kennington III     ProcStatEntry others;
1431285115cSWilliam A. Kennington III     others.cmdline = "(Others)";
1441285115cSWilliam A. Kennington III     others.utime = others.stime = 0;
1451285115cSWilliam A. Kennington III 
1461285115cSWilliam A. Kennington III     // Only show this many processes and aggregate all remaining ones into
1471285115cSWilliam A. Kennington III     // "others" in order to keep the size of the snapshot reasonably small.
1481285115cSWilliam A. Kennington III     // With 10 process stat entries and 10 FD count entries, the size of the
1491285115cSWilliam A. Kennington III     // snapshot reaches around 1.5KiB. This is non-trivial, and we have to set
1501285115cSWilliam A. Kennington III     // the collection interval long enough so as not to over-stress the IPMI
1511285115cSWilliam A. Kennington III     // interface and the data collection service. The value of 10 is chosen
1521285115cSWilliam A. Kennington III     // empirically, it might be subject to adjustments when the system is
1531285115cSWilliam A. Kennington III     // launched later.
1541285115cSWilliam A. Kennington III     constexpr int topN = 10;
1551285115cSWilliam A. Kennington III 
1561285115cSWilliam A. Kennington III     for (size_t i = 0; i < entries.size(); ++i)
1571285115cSWilliam A. Kennington III     {
1581285115cSWilliam A. Kennington III         if (i >= topN)
1591285115cSWilliam A. Kennington III         {
1601285115cSWilliam A. Kennington III             isOthers = true;
1611285115cSWilliam A. Kennington III         }
1621285115cSWilliam A. Kennington III 
1637f49370dSWilliam A. Kennington III         const ProcStatEntry& entry = entries[i];
1641285115cSWilliam A. Kennington III 
1651285115cSWilliam A. Kennington III         if (isOthers)
1661285115cSWilliam A. Kennington III         {
1671285115cSWilliam A. Kennington III             others.utime += entry.utime;
1681285115cSWilliam A. Kennington III             others.stime += entry.stime;
1691285115cSWilliam A. Kennington III         }
1701285115cSWilliam A. Kennington III         else
1711285115cSWilliam A. Kennington III         {
1721285115cSWilliam A. Kennington III             std::string fullCmdline = entry.cmdline;
1731285115cSWilliam A. Kennington III             if (entry.tcomm.size() > 0)
1741285115cSWilliam A. Kennington III             {
1757f49370dSWilliam A. Kennington III                 fullCmdline += " ";
1767f49370dSWilliam A. Kennington III                 fullCmdline += entry.tcomm;
1771285115cSWilliam A. Kennington III             }
1787f49370dSWilliam A. Kennington III             procs.emplace_back(
1797f49370dSWilliam A. Kennington III                 bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat{
1807f49370dSWilliam A. Kennington III                     .sidx_cmdline = obj.getStringID(fullCmdline),
1817f49370dSWilliam A. Kennington III                     .utime = entry.utime,
1827f49370dSWilliam A. Kennington III                     .stime = entry.stime,
1837f49370dSWilliam A. Kennington III                 });
1841285115cSWilliam A. Kennington III         }
1851285115cSWilliam A. Kennington III     }
1861285115cSWilliam A. Kennington III 
1871285115cSWilliam A. Kennington III     if (isOthers)
1881285115cSWilliam A. Kennington III     {
1897f49370dSWilliam A. Kennington III         procs.emplace_back(bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat{
1907f49370dSWilliam A. Kennington III             .sidx_cmdline = obj.getStringID(others.cmdline),
1917f49370dSWilliam A. Kennington III             .utime = others.utime,
1927f49370dSWilliam A. Kennington III             .stime = others.stime,
1937f49370dSWilliam A. Kennington III 
1947f49370dSWilliam A. Kennington III         });
1951285115cSWilliam A. Kennington III     }
1961285115cSWilliam A. Kennington III 
1977f49370dSWilliam A. Kennington III     use = true;
1987f49370dSWilliam A. Kennington III     return bmcmetrics_metricproto_BmcProcStatMetric{
1997f49370dSWilliam A. Kennington III         .stats = pbSubsEncoder<
2007f49370dSWilliam A. Kennington III             bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat_fields>(procs),
2017f49370dSWilliam A. Kennington III     };
2021285115cSWilliam A. Kennington III }
2031285115cSWilliam A. Kennington III 
2041285115cSWilliam A. Kennington III int getFdCount(int pid)
2051285115cSWilliam A. Kennington III {
2061285115cSWilliam A. Kennington III     const std::string& fdPath = "/proc/" + std::to_string(pid) + "/fd";
2071285115cSWilliam A. Kennington III     return std::distance(std::filesystem::directory_iterator(fdPath),
2081285115cSWilliam A. Kennington III                          std::filesystem::directory_iterator{});
2091285115cSWilliam A. Kennington III }
2101285115cSWilliam A. Kennington III 
2111285115cSWilliam A. Kennington III struct FdStatEntry
2121285115cSWilliam A. Kennington III {
2131285115cSWilliam A. Kennington III     int fdCount;
2141285115cSWilliam A. Kennington III     std::string cmdline;
2151285115cSWilliam A. Kennington III     std::string tcomm;
2161285115cSWilliam A. Kennington III 
2171285115cSWilliam A. Kennington III     // Processes with the largest fdCount goes first.
2181285115cSWilliam A. Kennington III     // Tie-breaking using cmdline then tcomm.
2191285115cSWilliam A. Kennington III     bool operator<(const FdStatEntry& other) const
2201285115cSWilliam A. Kennington III     {
2211285115cSWilliam A. Kennington III         const int negFdCount = -fdCount;
2221285115cSWilliam A. Kennington III         const int negOtherFdCount = -other.fdCount;
2231285115cSWilliam A. Kennington III         return std::tie(negFdCount, cmdline, tcomm) <
2241285115cSWilliam A. Kennington III                std::tie(negOtherFdCount, other.cmdline, other.tcomm);
2251285115cSWilliam A. Kennington III     }
2261285115cSWilliam A. Kennington III };
2271285115cSWilliam A. Kennington III 
2287f49370dSWilliam A. Kennington III static bmcmetrics_metricproto_BmcFdStatMetric getFdStatMetric(
2297f49370dSWilliam A. Kennington III     BmcHealthSnapshot& obj, long ticksPerSec,
2307f49370dSWilliam A. Kennington III     std::vector<bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat>& fds,
2317f49370dSWilliam A. Kennington III     bool& use) noexcept
2321285115cSWilliam A. Kennington III {
2337f49370dSWilliam A. Kennington III     if (ticksPerSec == 0)
2347f49370dSWilliam A. Kennington III     {
2357f49370dSWilliam A. Kennington III         return {};
2367f49370dSWilliam A. Kennington III     }
2371285115cSWilliam A. Kennington III 
2381285115cSWilliam A. Kennington III     // Sort by fd count, no tie-breaking
2391285115cSWilliam A. Kennington III     std::vector<FdStatEntry> entries;
2401285115cSWilliam A. Kennington III 
2411285115cSWilliam A. Kennington III     const std::string_view procPath = "/proc/";
2421285115cSWilliam A. Kennington III     for (const auto& procEntry : std::filesystem::directory_iterator(procPath))
2431285115cSWilliam A. Kennington III     {
2441285115cSWilliam A. Kennington III         const std::string& path = procEntry.path();
2451285115cSWilliam A. Kennington III         int pid = 0;
2461285115cSWilliam A. Kennington III         FdStatEntry entry;
2471285115cSWilliam A. Kennington III         if (isNumericPath(path, pid))
2481285115cSWilliam A. Kennington III         {
2491285115cSWilliam A. Kennington III             try
2501285115cSWilliam A. Kennington III             {
2511285115cSWilliam A. Kennington III                 entry.fdCount = getFdCount(pid);
2521285115cSWilliam A. Kennington III                 TcommUtimeStime t = getTcommUtimeStime(pid, ticksPerSec);
2531285115cSWilliam A. Kennington III                 entry.cmdline = getCmdLine(pid);
2541285115cSWilliam A. Kennington III                 entry.tcomm = t.tcomm;
2551285115cSWilliam A. Kennington III                 entries.push_back(entry);
2561285115cSWilliam A. Kennington III             }
2571285115cSWilliam A. Kennington III             catch (const std::exception& e)
2581285115cSWilliam A. Kennington III             {
2591285115cSWilliam A. Kennington III                 log<level::ERR>("Could not get file descriptor stats");
2601285115cSWilliam A. Kennington III             }
2611285115cSWilliam A. Kennington III         }
2621285115cSWilliam A. Kennington III     }
2631285115cSWilliam A. Kennington III 
2641285115cSWilliam A. Kennington III     std::sort(entries.begin(), entries.end());
2651285115cSWilliam A. Kennington III 
2661285115cSWilliam A. Kennington III     bool isOthers = false;
2671285115cSWilliam A. Kennington III 
2681285115cSWilliam A. Kennington III     // Only report the detailed fd count and cmdline for the top 10 entries,
2691285115cSWilliam A. Kennington III     // and collapse all others into "others".
2701285115cSWilliam A. Kennington III     constexpr int topN = 10;
2711285115cSWilliam A. Kennington III 
2721285115cSWilliam A. Kennington III     FdStatEntry others;
2731285115cSWilliam A. Kennington III     others.cmdline = "(Others)";
2741285115cSWilliam A. Kennington III     others.fdCount = 0;
2751285115cSWilliam A. Kennington III 
2761285115cSWilliam A. Kennington III     for (size_t i = 0; i < entries.size(); ++i)
2771285115cSWilliam A. Kennington III     {
2781285115cSWilliam A. Kennington III         if (i >= topN)
2791285115cSWilliam A. Kennington III         {
2801285115cSWilliam A. Kennington III             isOthers = true;
2811285115cSWilliam A. Kennington III         }
2821285115cSWilliam A. Kennington III 
2831285115cSWilliam A. Kennington III         const FdStatEntry& entry = entries[i];
2841285115cSWilliam A. Kennington III         if (isOthers)
2851285115cSWilliam A. Kennington III         {
2861285115cSWilliam A. Kennington III             others.fdCount += entry.fdCount;
2871285115cSWilliam A. Kennington III         }
2881285115cSWilliam A. Kennington III         else
2891285115cSWilliam A. Kennington III         {
2901285115cSWilliam A. Kennington III             std::string fullCmdline = entry.cmdline;
2911285115cSWilliam A. Kennington III             if (entry.tcomm.size() > 0)
2921285115cSWilliam A. Kennington III             {
2937f49370dSWilliam A. Kennington III                 fullCmdline += " ";
2947f49370dSWilliam A. Kennington III                 fullCmdline += entry.tcomm;
2951285115cSWilliam A. Kennington III             }
2967f49370dSWilliam A. Kennington III             fds.emplace_back(bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat{
2977f49370dSWilliam A. Kennington III                 .sidx_cmdline = obj.getStringID(fullCmdline),
2987f49370dSWilliam A. Kennington III                 .fd_count = entry.fdCount,
2997f49370dSWilliam A. Kennington III             });
3001285115cSWilliam A. Kennington III         }
3011285115cSWilliam A. Kennington III     }
3021285115cSWilliam A. Kennington III 
3031285115cSWilliam A. Kennington III     if (isOthers)
3041285115cSWilliam A. Kennington III     {
3057f49370dSWilliam A. Kennington III         fds.emplace_back(bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat{
3067f49370dSWilliam A. Kennington III             .sidx_cmdline = obj.getStringID(others.cmdline),
3077f49370dSWilliam A. Kennington III             .fd_count = others.fdCount,
3087f49370dSWilliam A. Kennington III         });
3091285115cSWilliam A. Kennington III     }
3101285115cSWilliam A. Kennington III 
3117f49370dSWilliam A. Kennington III     use = true;
3127f49370dSWilliam A. Kennington III     return bmcmetrics_metricproto_BmcFdStatMetric{
3137f49370dSWilliam A. Kennington III         .stats = pbSubsEncoder<
3147f49370dSWilliam A. Kennington III             bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat_fields>(fds),
3157f49370dSWilliam A. Kennington III     };
3167f49370dSWilliam A. Kennington III }
3177f49370dSWilliam A. Kennington III 
3184dba220dSWilly Tu static bmcmetrics_metricproto_BmcECCMetric getECCMetric(bool& use) noexcept
3194dba220dSWilly Tu {
3204dba220dSWilly Tu     EccCounts eccCounts;
3214dba220dSWilly Tu     use = getECCErrorCounts(eccCounts);
3224dba220dSWilly Tu     if (!use)
3234dba220dSWilly Tu     {
3244dba220dSWilly Tu         return {};
3254dba220dSWilly Tu     }
3264dba220dSWilly Tu     return bmcmetrics_metricproto_BmcECCMetric{
3274dba220dSWilly Tu         .correctable_error_count = eccCounts.correctableErrCount,
3284dba220dSWilly Tu         .uncorrectable_error_count = eccCounts.uncorrectableErrCount,
3294dba220dSWilly Tu     };
3304dba220dSWilly Tu }
3314dba220dSWilly Tu 
3327f49370dSWilliam A. Kennington III static bmcmetrics_metricproto_BmcMemoryMetric getMemMetric() noexcept
3337f49370dSWilliam A. Kennington III {
3347f49370dSWilliam A. Kennington III     bmcmetrics_metricproto_BmcMemoryMetric ret = {};
3357f49370dSWilliam A. Kennington III     auto data = readFileThenGrepIntoString("/proc/meminfo");
3367f49370dSWilliam A. Kennington III     int value;
3377f49370dSWilliam A. Kennington III     if (parseMeminfoValue(data, "MemAvailable:", value))
3387f49370dSWilliam A. Kennington III     {
3397f49370dSWilliam A. Kennington III         ret.mem_available = value;
3407f49370dSWilliam A. Kennington III     }
3417f49370dSWilliam A. Kennington III     if (parseMeminfoValue(data, "Slab:", value))
3427f49370dSWilliam A. Kennington III     {
3437f49370dSWilliam A. Kennington III         ret.slab = value;
3447f49370dSWilliam A. Kennington III     }
3457f49370dSWilliam A. Kennington III 
3467f49370dSWilliam A. Kennington III     if (parseMeminfoValue(data, "KernelStack:", value))
3477f49370dSWilliam A. Kennington III     {
3487f49370dSWilliam A. Kennington III         ret.kernel_stack = value;
3497f49370dSWilliam A. Kennington III     }
3501285115cSWilliam A. Kennington III     return ret;
3511285115cSWilliam A. Kennington III }
3521285115cSWilliam A. Kennington III 
3537f49370dSWilliam A. Kennington III static bmcmetrics_metricproto_BmcUptimeMetric
3547f49370dSWilliam A. Kennington III     getUptimeMetric(bool& use) noexcept
3551285115cSWilliam A. Kennington III {
3567f49370dSWilliam A. Kennington III     bmcmetrics_metricproto_BmcUptimeMetric ret = {};
3571285115cSWilliam A. Kennington III 
358b63d6314SMichael Shen     double uptime = 0;
3597f49370dSWilliam A. Kennington III     {
3607f49370dSWilliam A. Kennington III         auto data = readFileThenGrepIntoString("/proc/uptime");
361b63d6314SMichael Shen         double idleProcessTime = 0;
3627f49370dSWilliam A. Kennington III         if (!parseProcUptime(data, uptime, idleProcessTime))
363b63d6314SMichael Shen         {
364b63d6314SMichael Shen             log<level::ERR>("Error parsing /proc/uptime");
3657f49370dSWilliam A. Kennington III             return ret;
366b63d6314SMichael Shen         }
3677f49370dSWilliam A. Kennington III         ret.uptime = uptime;
3687f49370dSWilliam A. Kennington III         ret.idle_process_time = idleProcessTime;
3691285115cSWilliam A. Kennington III     }
3701285115cSWilliam A. Kennington III 
3717f49370dSWilliam A. Kennington III     BootTimesMonotonic btm;
3727f49370dSWilliam A. Kennington III     if (!getBootTimesMonotonic(btm))
3737f49370dSWilliam A. Kennington III     {
3747f49370dSWilliam A. Kennington III         log<level::ERR>("Could not get boot time");
3757f49370dSWilliam A. Kennington III         return ret;
3767f49370dSWilliam A. Kennington III     }
3777f49370dSWilliam A. Kennington III     if (btm.firmwareTime == 0 && btm.powerOnSecCounterTime != 0)
3787f49370dSWilliam A. Kennington III     {
3797f49370dSWilliam A. Kennington III         ret.firmware_boot_time_sec =
3807f49370dSWilliam A. Kennington III             static_cast<double>(btm.powerOnSecCounterTime) - uptime;
3817f49370dSWilliam A. Kennington III     }
3827f49370dSWilliam A. Kennington III     else
3837f49370dSWilliam A. Kennington III     {
3847f49370dSWilliam A. Kennington III         ret.firmware_boot_time_sec =
3857f49370dSWilliam A. Kennington III             static_cast<double>(btm.firmwareTime - btm.loaderTime) / 1e6;
3867f49370dSWilliam A. Kennington III     }
3877f49370dSWilliam A. Kennington III     ret.loader_boot_time_sec = static_cast<double>(btm.loaderTime) / 1e6;
3887f49370dSWilliam A. Kennington III     if (btm.initrdTime != 0)
3897f49370dSWilliam A. Kennington III     {
3907f49370dSWilliam A. Kennington III         ret.kernel_boot_time_sec = static_cast<double>(btm.initrdTime) / 1e6;
3917f49370dSWilliam A. Kennington III         ret.initrd_boot_time_sec =
3927f49370dSWilliam A. Kennington III             static_cast<double>(btm.userspaceTime - btm.initrdTime) / 1e6;
3937f49370dSWilliam A. Kennington III         ret.userspace_boot_time_sec =
3947f49370dSWilliam A. Kennington III             static_cast<double>(btm.finishTime - btm.userspaceTime) / 1e6;
3957f49370dSWilliam A. Kennington III     }
3967f49370dSWilliam A. Kennington III     else
3977f49370dSWilliam A. Kennington III     {
3987f49370dSWilliam A. Kennington III         ret.kernel_boot_time_sec = static_cast<double>(btm.userspaceTime) / 1e6;
3997f49370dSWilliam A. Kennington III         ret.initrd_boot_time_sec = 0;
4007f49370dSWilliam A. Kennington III         ret.userspace_boot_time_sec =
4017f49370dSWilliam A. Kennington III             static_cast<double>(btm.finishTime - btm.userspaceTime) / 1e6;
4027f49370dSWilliam A. Kennington III     }
4037f49370dSWilliam A. Kennington III 
4047f49370dSWilliam A. Kennington III     use = true;
4057f49370dSWilliam A. Kennington III     return ret;
4067f49370dSWilliam A. Kennington III }
4077f49370dSWilliam A. Kennington III 
4087f49370dSWilliam A. Kennington III static bmcmetrics_metricproto_BmcDiskSpaceMetric
4097f49370dSWilliam A. Kennington III     getStorageMetric(bool& use) noexcept
4107f49370dSWilliam A. Kennington III {
4117f49370dSWilliam A. Kennington III     bmcmetrics_metricproto_BmcDiskSpaceMetric ret = {};
4121285115cSWilliam A. Kennington III     struct statvfs fiData;
4137f49370dSWilliam A. Kennington III     if (statvfs("/", &fiData) < 0)
4141285115cSWilliam A. Kennington III     {
4151285115cSWilliam A. Kennington III         log<level::ERR>("Could not call statvfs");
4161285115cSWilliam A. Kennington III     }
4171285115cSWilliam A. Kennington III     else
4181285115cSWilliam A. Kennington III     {
4197f49370dSWilliam A. Kennington III         ret.rwfs_kib_available = (fiData.f_bsize * fiData.f_bfree) / 1024;
4207f49370dSWilliam A. Kennington III         use = true;
4217f49370dSWilliam A. Kennington III     }
4227f49370dSWilliam A. Kennington III     return ret;
4231285115cSWilliam A. Kennington III }
4241285115cSWilliam A. Kennington III 
4257f49370dSWilliam A. Kennington III void BmcHealthSnapshot::doWork()
4267f49370dSWilliam A. Kennington III {
4271285115cSWilliam A. Kennington III     // The next metrics require a sane ticks_per_sec value, typically 100 on
4281285115cSWilliam A. Kennington III     // the BMC. In the very rare circumstance when it's 0, exit early and return
4291285115cSWilliam A. Kennington III     // a partially complete snapshot (no process).
4301285115cSWilliam A. Kennington III     ticksPerSec = getTicksPerSec();
4311285115cSWilliam A. Kennington III 
4327f49370dSWilliam A. Kennington III     static constexpr auto stcb = [](pb_ostream_t* stream,
4337f49370dSWilliam A. Kennington III                                     const pb_field_t* field,
4347f49370dSWilliam A. Kennington III                                     void* const* arg) noexcept {
4357f49370dSWilliam A. Kennington III         auto& self = *reinterpret_cast<BmcHealthSnapshot*>(*arg);
4367f49370dSWilliam A. Kennington III         std::vector<std::string_view> strs(self.stringTable.size());
4377f49370dSWilliam A. Kennington III         for (const auto& [str, i] : self.stringTable)
4381285115cSWilliam A. Kennington III         {
4397f49370dSWilliam A. Kennington III             strs[i] = str;
4407f49370dSWilliam A. Kennington III         }
4417f49370dSWilliam A. Kennington III         for (auto& str : strs)
4427f49370dSWilliam A. Kennington III         {
4437f49370dSWilliam A. Kennington III             bmcmetrics_metricproto_BmcStringTable_StringEntry msg = {
4447f49370dSWilliam A. Kennington III                 .value = pbStrEncoder(str),
4457f49370dSWilliam A. Kennington III             };
4467f49370dSWilliam A. Kennington III             if (!pb_encode_tag_for_field(stream, field) ||
4477f49370dSWilliam A. Kennington III                 !pb_encode_submessage(
4487f49370dSWilliam A. Kennington III                     stream,
4497f49370dSWilliam A. Kennington III                     bmcmetrics_metricproto_BmcStringTable_StringEntry_fields,
4507f49370dSWilliam A. Kennington III                     &msg))
4517f49370dSWilliam A. Kennington III             {
4527f49370dSWilliam A. Kennington III                 return false;
4537f49370dSWilliam A. Kennington III             }
4547f49370dSWilliam A. Kennington III         }
4557f49370dSWilliam A. Kennington III         return true;
4567f49370dSWilliam A. Kennington III     };
4577f49370dSWilliam A. Kennington III     std::vector<bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat> procs;
4587f49370dSWilliam A. Kennington III     std::vector<bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat> fds;
4597f49370dSWilliam A. Kennington III     bmcmetrics_metricproto_BmcMetricSnapshot snapshot = {
4607f49370dSWilliam A. Kennington III         .has_string_table = true,
4617f49370dSWilliam A. Kennington III         .string_table =
4627f49370dSWilliam A. Kennington III             {
4637f49370dSWilliam A. Kennington III                 .entries = {{.encode = stcb}, this},
4647f49370dSWilliam A. Kennington III             },
4657f49370dSWilliam A. Kennington III         .has_memory_metric = true,
4667f49370dSWilliam A. Kennington III         .memory_metric = getMemMetric(),
4677f49370dSWilliam A. Kennington III         .has_uptime_metric = false,
4687f49370dSWilliam A. Kennington III         .uptime_metric = getUptimeMetric(snapshot.has_uptime_metric),
4697f49370dSWilliam A. Kennington III         .has_storage_space_metric = false,
4707f49370dSWilliam A. Kennington III         .storage_space_metric =
4717f49370dSWilliam A. Kennington III             getStorageMetric(snapshot.has_storage_space_metric),
4727f49370dSWilliam A. Kennington III         .has_procstat_metric = false,
4737f49370dSWilliam A. Kennington III         .procstat_metric = getProcStatMetric(*this, ticksPerSec, procs,
4747f49370dSWilliam A. Kennington III                                              snapshot.has_procstat_metric),
4757f49370dSWilliam A. Kennington III         .has_fdstat_metric = false,
4767f49370dSWilliam A. Kennington III         .fdstat_metric = getFdStatMetric(*this, ticksPerSec, fds,
4777f49370dSWilliam A. Kennington III                                          snapshot.has_fdstat_metric),
4784dba220dSWilly Tu         .has_ecc_metric = false,
4794dba220dSWilly Tu         .ecc_metric = getECCMetric(snapshot.has_ecc_metric),
4807f49370dSWilliam A. Kennington III     };
4817f49370dSWilliam A. Kennington III     pb_ostream_t nost = {};
4827f49370dSWilliam A. Kennington III     if (!pb_encode(&nost, bmcmetrics_metricproto_BmcMetricSnapshot_fields,
4837f49370dSWilliam A. Kennington III                    &snapshot))
4847f49370dSWilliam A. Kennington III     {
4857f49370dSWilliam A. Kennington III         auto msg = std::format("Getting pb size: {}", PB_GET_ERROR(&nost));
4867f49370dSWilliam A. Kennington III         log<level::ERR>(msg.c_str());
4871285115cSWilliam A. Kennington III         return;
4881285115cSWilliam A. Kennington III     }
4897f49370dSWilliam A. Kennington III     pbDump.resize(nost.bytes_written);
4907f49370dSWilliam A. Kennington III     auto ost = pb_ostream_from_buffer(
4917f49370dSWilliam A. Kennington III         reinterpret_cast<pb_byte_t*>(pbDump.data()), pbDump.size());
4927f49370dSWilliam A. Kennington III     if (!pb_encode(&ost, bmcmetrics_metricproto_BmcMetricSnapshot_fields,
4937f49370dSWilliam A. Kennington III                    &snapshot))
4941285115cSWilliam A. Kennington III     {
4957f49370dSWilliam A. Kennington III         auto msg = std::format("Writing pb msg: {}", PB_GET_ERROR(&ost));
4967f49370dSWilliam A. Kennington III         log<level::ERR>(msg.c_str());
4977f49370dSWilliam A. Kennington III         return;
4981285115cSWilliam A. Kennington III     }
4991285115cSWilliam A. Kennington III     done = true;
5001285115cSWilliam A. Kennington III }
5011285115cSWilliam A. Kennington III 
5021285115cSWilliam A. Kennington III // BmcBlobSessionStat (9) but passing meta as reference instead of pointer,
5031285115cSWilliam A. Kennington III // since the metadata must not be null at this point.
5041285115cSWilliam A. Kennington III bool BmcHealthSnapshot::stat(blobs::BlobMeta& meta)
5051285115cSWilliam A. Kennington III {
5061285115cSWilliam A. Kennington III     if (!done)
5071285115cSWilliam A. Kennington III     {
5081285115cSWilliam A. Kennington III         // Bits 8~15 are blob-specific state flags.
5091285115cSWilliam A. Kennington III         // For this blob, bit 8 is set when metric collection is still in
5101285115cSWilliam A. Kennington III         // progress.
5111285115cSWilliam A. Kennington III         meta.blobState |= (1 << 8);
5121285115cSWilliam A. Kennington III     }
5131285115cSWilliam A. Kennington III     else
5141285115cSWilliam A. Kennington III     {
5151285115cSWilliam A. Kennington III         meta.blobState = 0;
5161285115cSWilliam A. Kennington III         meta.blobState = blobs::StateFlags::open_read;
5171285115cSWilliam A. Kennington III         meta.size = pbDump.size();
5181285115cSWilliam A. Kennington III     }
5191285115cSWilliam A. Kennington III     return true;
5201285115cSWilliam A. Kennington III }
5211285115cSWilliam A. Kennington III 
5221285115cSWilliam A. Kennington III std::string_view BmcHealthSnapshot::read(uint32_t offset,
5231285115cSWilliam A. Kennington III                                          uint32_t requestedSize)
5241285115cSWilliam A. Kennington III {
5251285115cSWilliam A. Kennington III     uint32_t size = static_cast<uint32_t>(pbDump.size());
5261285115cSWilliam A. Kennington III     if (offset >= size)
5271285115cSWilliam A. Kennington III     {
5281285115cSWilliam A. Kennington III         return {};
5291285115cSWilliam A. Kennington III     }
5301285115cSWilliam A. Kennington III     return std::string_view(pbDump.data() + offset,
5311285115cSWilliam A. Kennington III                             std::min(requestedSize, size - offset));
5321285115cSWilliam A. Kennington III }
5331285115cSWilliam A. Kennington III 
5341285115cSWilliam A. Kennington III int BmcHealthSnapshot::getStringID(const std::string_view s)
5351285115cSWilliam A. Kennington III {
5361285115cSWilliam A. Kennington III     int ret = 0;
5371285115cSWilliam A. Kennington III     auto itr = stringTable.find(s.data());
5381285115cSWilliam A. Kennington III     if (itr == stringTable.end())
5391285115cSWilliam A. Kennington III     {
5401285115cSWilliam A. Kennington III         stringTable[s.data()] = stringId;
5411285115cSWilliam A. Kennington III         ret = stringId;
5421285115cSWilliam A. Kennington III         ++stringId;
5431285115cSWilliam A. Kennington III     }
5441285115cSWilliam A. Kennington III     else
5451285115cSWilliam A. Kennington III     {
5461285115cSWilliam A. Kennington III         ret = itr->second;
5471285115cSWilliam A. Kennington III     }
5481285115cSWilliam A. Kennington III     return ret;
5491285115cSWilliam A. Kennington III }
5501285115cSWilliam A. Kennington III 
5511285115cSWilliam A. Kennington III } // namespace metric_blob
552