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