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