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
BmcHealthSnapshot()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>
44c66ebc35SPatrick Williams static constexpr auto pbEncodeStr =
45c66ebc35SPatrick Williams [](pb_ostream_t* stream, const pb_field_iter_t* field,
__anon3af598100102(pb_ostream_t* stream, const pb_field_iter_t* field, void* const* arg) 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) &&
50c66ebc35SPatrick Williams pb_encode_string(stream,
51c66ebc35SPatrick Williams reinterpret_cast<const pb_byte_t*>(s.data()),
52c66ebc35SPatrick Williams s.size());
537f49370dSWilliam A. Kennington III };
547f49370dSWilliam A. Kennington III
557f49370dSWilliam A. Kennington III template <typename T>
pbStrEncoder(const T & 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>
62c66ebc35SPatrick Williams static constexpr auto pbEncodeSubs =
63c66ebc35SPatrick Williams [](pb_ostream_t* stream, const pb_field_iter_t* field,
__anon3af598100202(pb_ostream_t* stream, const pb_field_iter_t* field, void* const* arg) 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>
pbSubsEncoder(const std::vector<T> & 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.
operator <metric_blob::ProcStatEntry921285115cSWilliam 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
getProcStatMetric(BmcHealthSnapshot & obj,long ticksPerSec,std::vector<bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat> & procs,bool & use)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
getFdCount(int pid)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.
operator <metric_blob::FdStatEntry2191285115cSWilliam 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
getFdStatMetric(BmcHealthSnapshot & obj,long ticksPerSec,std::vector<bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat> & fds,bool & use)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
getECCMetric(bool & use)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
getMemMetric()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
getUptimeMetric(bool & use)353*6c41aba5SPatrick Williams static bmcmetrics_metricproto_BmcUptimeMetric getUptimeMetric(
354*6c41aba5SPatrick Williams 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
getStorageMetric(bool & use)408*6c41aba5SPatrick Williams static bmcmetrics_metricproto_BmcDiskSpaceMetric getStorageMetric(
409*6c41aba5SPatrick Williams bool& use) noexcept
4107f49370dSWilliam A. Kennington III {
4117f49370dSWilliam A. Kennington III bmcmetrics_metricproto_BmcDiskSpaceMetric ret = {};
412119e7928SAbby struct statvfs rwFiData, tmpFiData;
413119e7928SAbby if (statvfs("/", &rwFiData) < 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 {
419119e7928SAbby ret.rwfs_kib_available = (rwFiData.f_bsize * rwFiData.f_bfree) / 1024;
420119e7928SAbby use = true;
421119e7928SAbby }
422119e7928SAbby if (statvfs("/tmp", &tmpFiData) < 0)
423119e7928SAbby {
424119e7928SAbby log<level::ERR>("Could not call statvfs");
425119e7928SAbby }
426119e7928SAbby else
427119e7928SAbby {
428119e7928SAbby ret.tmpfs_kib_available =
429119e7928SAbby (tmpFiData.f_bsize * tmpFiData.f_bfree) / 1024;
4307f49370dSWilliam A. Kennington III use = true;
4317f49370dSWilliam A. Kennington III }
4327f49370dSWilliam A. Kennington III return ret;
4331285115cSWilliam A. Kennington III }
4341285115cSWilliam A. Kennington III
doWork()4357f49370dSWilliam A. Kennington III void BmcHealthSnapshot::doWork()
4367f49370dSWilliam A. Kennington III {
4371285115cSWilliam A. Kennington III // The next metrics require a sane ticks_per_sec value, typically 100 on
4381285115cSWilliam A. Kennington III // the BMC. In the very rare circumstance when it's 0, exit early and return
4391285115cSWilliam A. Kennington III // a partially complete snapshot (no process).
4401285115cSWilliam A. Kennington III ticksPerSec = getTicksPerSec();
4411285115cSWilliam A. Kennington III
4427f49370dSWilliam A. Kennington III static constexpr auto stcb = [](pb_ostream_t* stream,
4437f49370dSWilliam A. Kennington III const pb_field_t* field,
4447f49370dSWilliam A. Kennington III void* const* arg) noexcept {
4457f49370dSWilliam A. Kennington III auto& self = *reinterpret_cast<BmcHealthSnapshot*>(*arg);
4467f49370dSWilliam A. Kennington III std::vector<std::string_view> strs(self.stringTable.size());
4477f49370dSWilliam A. Kennington III for (const auto& [str, i] : self.stringTable)
4481285115cSWilliam A. Kennington III {
4497f49370dSWilliam A. Kennington III strs[i] = str;
4507f49370dSWilliam A. Kennington III }
4517f49370dSWilliam A. Kennington III for (auto& str : strs)
4527f49370dSWilliam A. Kennington III {
4537f49370dSWilliam A. Kennington III bmcmetrics_metricproto_BmcStringTable_StringEntry msg = {
4547f49370dSWilliam A. Kennington III .value = pbStrEncoder(str),
4557f49370dSWilliam A. Kennington III };
4567f49370dSWilliam A. Kennington III if (!pb_encode_tag_for_field(stream, field) ||
4577f49370dSWilliam A. Kennington III !pb_encode_submessage(
4587f49370dSWilliam A. Kennington III stream,
4597f49370dSWilliam A. Kennington III bmcmetrics_metricproto_BmcStringTable_StringEntry_fields,
4607f49370dSWilliam A. Kennington III &msg))
4617f49370dSWilliam A. Kennington III {
4627f49370dSWilliam A. Kennington III return false;
4637f49370dSWilliam A. Kennington III }
4647f49370dSWilliam A. Kennington III }
4657f49370dSWilliam A. Kennington III return true;
4667f49370dSWilliam A. Kennington III };
4677f49370dSWilliam A. Kennington III std::vector<bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat> procs;
4687f49370dSWilliam A. Kennington III std::vector<bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat> fds;
4697f49370dSWilliam A. Kennington III bmcmetrics_metricproto_BmcMetricSnapshot snapshot = {
4707f49370dSWilliam A. Kennington III .has_string_table = true,
4717f49370dSWilliam A. Kennington III .string_table =
4727f49370dSWilliam A. Kennington III {
4737f49370dSWilliam A. Kennington III .entries = {{.encode = stcb}, this},
4747f49370dSWilliam A. Kennington III },
4757f49370dSWilliam A. Kennington III .has_memory_metric = true,
4767f49370dSWilliam A. Kennington III .memory_metric = getMemMetric(),
4777f49370dSWilliam A. Kennington III .has_uptime_metric = false,
4787f49370dSWilliam A. Kennington III .uptime_metric = getUptimeMetric(snapshot.has_uptime_metric),
4797f49370dSWilliam A. Kennington III .has_storage_space_metric = false,
4807f49370dSWilliam A. Kennington III .storage_space_metric =
4817f49370dSWilliam A. Kennington III getStorageMetric(snapshot.has_storage_space_metric),
4827f49370dSWilliam A. Kennington III .has_procstat_metric = false,
4837f49370dSWilliam A. Kennington III .procstat_metric = getProcStatMetric(*this, ticksPerSec, procs,
4847f49370dSWilliam A. Kennington III snapshot.has_procstat_metric),
4857f49370dSWilliam A. Kennington III .has_fdstat_metric = false,
4867f49370dSWilliam A. Kennington III .fdstat_metric = getFdStatMetric(*this, ticksPerSec, fds,
4877f49370dSWilliam A. Kennington III snapshot.has_fdstat_metric),
4884dba220dSWilly Tu .has_ecc_metric = false,
4894dba220dSWilly Tu .ecc_metric = getECCMetric(snapshot.has_ecc_metric),
4907f49370dSWilliam A. Kennington III };
4917f49370dSWilliam A. Kennington III pb_ostream_t nost = {};
4927f49370dSWilliam A. Kennington III if (!pb_encode(&nost, bmcmetrics_metricproto_BmcMetricSnapshot_fields,
4937f49370dSWilliam A. Kennington III &snapshot))
4947f49370dSWilliam A. Kennington III {
4957f49370dSWilliam A. Kennington III auto msg = std::format("Getting pb size: {}", PB_GET_ERROR(&nost));
4967f49370dSWilliam A. Kennington III log<level::ERR>(msg.c_str());
4971285115cSWilliam A. Kennington III return;
4981285115cSWilliam A. Kennington III }
4997f49370dSWilliam A. Kennington III pbDump.resize(nost.bytes_written);
5007f49370dSWilliam A. Kennington III auto ost = pb_ostream_from_buffer(
5017f49370dSWilliam A. Kennington III reinterpret_cast<pb_byte_t*>(pbDump.data()), pbDump.size());
5027f49370dSWilliam A. Kennington III if (!pb_encode(&ost, bmcmetrics_metricproto_BmcMetricSnapshot_fields,
5037f49370dSWilliam A. Kennington III &snapshot))
5041285115cSWilliam A. Kennington III {
5057f49370dSWilliam A. Kennington III auto msg = std::format("Writing pb msg: {}", PB_GET_ERROR(&ost));
5067f49370dSWilliam A. Kennington III log<level::ERR>(msg.c_str());
5077f49370dSWilliam A. Kennington III return;
5081285115cSWilliam A. Kennington III }
5091285115cSWilliam A. Kennington III done = true;
5101285115cSWilliam A. Kennington III }
5111285115cSWilliam A. Kennington III
5121285115cSWilliam A. Kennington III // BmcBlobSessionStat (9) but passing meta as reference instead of pointer,
5131285115cSWilliam A. Kennington III // since the metadata must not be null at this point.
stat(blobs::BlobMeta & meta)5141285115cSWilliam A. Kennington III bool BmcHealthSnapshot::stat(blobs::BlobMeta& meta)
5151285115cSWilliam A. Kennington III {
5161285115cSWilliam A. Kennington III if (!done)
5171285115cSWilliam A. Kennington III {
5181285115cSWilliam A. Kennington III // Bits 8~15 are blob-specific state flags.
5191285115cSWilliam A. Kennington III // For this blob, bit 8 is set when metric collection is still in
5201285115cSWilliam A. Kennington III // progress.
5211285115cSWilliam A. Kennington III meta.blobState |= (1 << 8);
5221285115cSWilliam A. Kennington III }
5231285115cSWilliam A. Kennington III else
5241285115cSWilliam A. Kennington III {
5251285115cSWilliam A. Kennington III meta.blobState = 0;
5261285115cSWilliam A. Kennington III meta.blobState = blobs::StateFlags::open_read;
5271285115cSWilliam A. Kennington III meta.size = pbDump.size();
5281285115cSWilliam A. Kennington III }
5291285115cSWilliam A. Kennington III return true;
5301285115cSWilliam A. Kennington III }
5311285115cSWilliam A. Kennington III
read(uint32_t offset,uint32_t requestedSize)5321285115cSWilliam A. Kennington III std::string_view BmcHealthSnapshot::read(uint32_t offset,
5331285115cSWilliam A. Kennington III uint32_t requestedSize)
5341285115cSWilliam A. Kennington III {
5351285115cSWilliam A. Kennington III uint32_t size = static_cast<uint32_t>(pbDump.size());
5361285115cSWilliam A. Kennington III if (offset >= size)
5371285115cSWilliam A. Kennington III {
5381285115cSWilliam A. Kennington III return {};
5391285115cSWilliam A. Kennington III }
5401285115cSWilliam A. Kennington III return std::string_view(pbDump.data() + offset,
5411285115cSWilliam A. Kennington III std::min(requestedSize, size - offset));
5421285115cSWilliam A. Kennington III }
5431285115cSWilliam A. Kennington III
getStringID(const std::string_view s)5441285115cSWilliam A. Kennington III int BmcHealthSnapshot::getStringID(const std::string_view s)
5451285115cSWilliam A. Kennington III {
5461285115cSWilliam A. Kennington III int ret = 0;
5471285115cSWilliam A. Kennington III auto itr = stringTable.find(s.data());
5481285115cSWilliam A. Kennington III if (itr == stringTable.end())
5491285115cSWilliam A. Kennington III {
5501285115cSWilliam A. Kennington III stringTable[s.data()] = stringId;
5511285115cSWilliam A. Kennington III ret = stringId;
5521285115cSWilliam A. Kennington III ++stringId;
5531285115cSWilliam A. Kennington III }
5541285115cSWilliam A. Kennington III else
5551285115cSWilliam A. Kennington III {
5561285115cSWilliam A. Kennington III ret = itr->second;
5571285115cSWilliam A. Kennington III }
5581285115cSWilliam A. Kennington III return ret;
5591285115cSWilliam A. Kennington III }
5601285115cSWilliam A. Kennington III
5611285115cSWilliam A. Kennington III } // namespace metric_blob
562