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 17*7f49370dSWilliam A. Kennington III #include "metricblob.pb.n.h" 181285115cSWilliam A. Kennington III 191285115cSWilliam A. Kennington III #include "util.hpp" 201285115cSWilliam A. Kennington III 21*7f49370dSWilliam 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 43*7f49370dSWilliam A. Kennington III template <typename T> 44*7f49370dSWilliam A. Kennington III static constexpr auto pbEncodeStr = [](pb_ostream_t* stream, 45*7f49370dSWilliam A. Kennington III const pb_field_iter_t* field, 46*7f49370dSWilliam A. Kennington III void* const* arg) noexcept { 47*7f49370dSWilliam A. Kennington III static_assert(sizeof(*std::declval<T>().data()) == sizeof(pb_byte_t)); 48*7f49370dSWilliam A. Kennington III const auto& s = *reinterpret_cast<const T*>(*arg); 49*7f49370dSWilliam A. Kennington III return pb_encode_tag_for_field(stream, field) && 50*7f49370dSWilliam A. Kennington III pb_encode_string( 51*7f49370dSWilliam A. Kennington III stream, reinterpret_cast<const pb_byte_t*>(s.data()), s.size()); 52*7f49370dSWilliam A. Kennington III }; 53*7f49370dSWilliam A. Kennington III 54*7f49370dSWilliam A. Kennington III template <typename T> 55*7f49370dSWilliam A. Kennington III static pb_callback_t pbStrEncoder(const T& t) noexcept 56*7f49370dSWilliam A. Kennington III { 57*7f49370dSWilliam A. Kennington III return {{.encode = pbEncodeStr<T>}, const_cast<T*>(&t)}; 58*7f49370dSWilliam A. Kennington III } 59*7f49370dSWilliam A. Kennington III 60*7f49370dSWilliam A. Kennington III template <auto fields, typename T> 61*7f49370dSWilliam A. Kennington III static constexpr auto pbEncodeSubs = [](pb_ostream_t* stream, 62*7f49370dSWilliam A. Kennington III const pb_field_iter_t* field, 63*7f49370dSWilliam A. Kennington III void* const* arg) noexcept { 64*7f49370dSWilliam A. Kennington III for (const auto& sub : *reinterpret_cast<const std::vector<T>*>(*arg)) 65*7f49370dSWilliam A. Kennington III { 66*7f49370dSWilliam A. Kennington III if (!pb_encode_tag_for_field(stream, field) || 67*7f49370dSWilliam A. Kennington III !pb_encode_submessage(stream, fields, &sub)) 68*7f49370dSWilliam A. Kennington III { 69*7f49370dSWilliam A. Kennington III return false; 70*7f49370dSWilliam A. Kennington III } 71*7f49370dSWilliam A. Kennington III } 72*7f49370dSWilliam A. Kennington III return true; 73*7f49370dSWilliam A. Kennington III }; 74*7f49370dSWilliam A. Kennington III 75*7f49370dSWilliam A. Kennington III template <auto fields, typename T> 76*7f49370dSWilliam A. Kennington III static pb_callback_t pbSubsEncoder(const std::vector<T>& t) 77*7f49370dSWilliam A. Kennington III { 78*7f49370dSWilliam A. Kennington III return {{.encode = pbEncodeSubs<fields, T>}, 79*7f49370dSWilliam A. Kennington III const_cast<std::vector<T>*>(&t)}; 80*7f49370dSWilliam A. Kennington III } 81*7f49370dSWilliam 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 100*7f49370dSWilliam A. Kennington III static bmcmetrics_metricproto_BmcProcStatMetric getProcStatMetric( 101*7f49370dSWilliam A. Kennington III BmcHealthSnapshot& obj, long ticksPerSec, 102*7f49370dSWilliam A. Kennington III std::vector<bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat>& procs, 103*7f49370dSWilliam A. Kennington III bool& use) noexcept 1041285115cSWilliam A. Kennington III { 105*7f49370dSWilliam A. Kennington III if (ticksPerSec == 0) 106*7f49370dSWilliam A. Kennington III { 107*7f49370dSWilliam A. Kennington III return {}; 108*7f49370dSWilliam 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 162*7f49370dSWilliam 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 { 174*7f49370dSWilliam A. Kennington III fullCmdline += " "; 175*7f49370dSWilliam A. Kennington III fullCmdline += entry.tcomm; 1761285115cSWilliam A. Kennington III } 177*7f49370dSWilliam A. Kennington III procs.emplace_back( 178*7f49370dSWilliam A. Kennington III bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat{ 179*7f49370dSWilliam A. Kennington III .sidx_cmdline = obj.getStringID(fullCmdline), 180*7f49370dSWilliam A. Kennington III .utime = entry.utime, 181*7f49370dSWilliam A. Kennington III .stime = entry.stime, 182*7f49370dSWilliam 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 { 188*7f49370dSWilliam A. Kennington III procs.emplace_back(bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat{ 189*7f49370dSWilliam A. Kennington III .sidx_cmdline = obj.getStringID(others.cmdline), 190*7f49370dSWilliam A. Kennington III .utime = others.utime, 191*7f49370dSWilliam A. Kennington III .stime = others.stime, 192*7f49370dSWilliam A. Kennington III 193*7f49370dSWilliam A. Kennington III }); 1941285115cSWilliam A. Kennington III } 1951285115cSWilliam A. Kennington III 196*7f49370dSWilliam A. Kennington III use = true; 197*7f49370dSWilliam A. Kennington III return bmcmetrics_metricproto_BmcProcStatMetric{ 198*7f49370dSWilliam A. Kennington III .stats = pbSubsEncoder< 199*7f49370dSWilliam A. Kennington III bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat_fields>(procs), 200*7f49370dSWilliam 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 227*7f49370dSWilliam A. Kennington III static bmcmetrics_metricproto_BmcFdStatMetric getFdStatMetric( 228*7f49370dSWilliam A. Kennington III BmcHealthSnapshot& obj, long ticksPerSec, 229*7f49370dSWilliam A. Kennington III std::vector<bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat>& fds, 230*7f49370dSWilliam A. Kennington III bool& use) noexcept 2311285115cSWilliam A. Kennington III { 232*7f49370dSWilliam A. Kennington III if (ticksPerSec == 0) 233*7f49370dSWilliam A. Kennington III { 234*7f49370dSWilliam A. Kennington III return {}; 235*7f49370dSWilliam 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 { 292*7f49370dSWilliam A. Kennington III fullCmdline += " "; 293*7f49370dSWilliam A. Kennington III fullCmdline += entry.tcomm; 2941285115cSWilliam A. Kennington III } 295*7f49370dSWilliam A. Kennington III fds.emplace_back(bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat{ 296*7f49370dSWilliam A. Kennington III .sidx_cmdline = obj.getStringID(fullCmdline), 297*7f49370dSWilliam A. Kennington III .fd_count = entry.fdCount, 298*7f49370dSWilliam 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 { 304*7f49370dSWilliam A. Kennington III fds.emplace_back(bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat{ 305*7f49370dSWilliam A. Kennington III .sidx_cmdline = obj.getStringID(others.cmdline), 306*7f49370dSWilliam A. Kennington III .fd_count = others.fdCount, 307*7f49370dSWilliam A. Kennington III }); 3081285115cSWilliam A. Kennington III } 3091285115cSWilliam A. Kennington III 310*7f49370dSWilliam A. Kennington III use = true; 311*7f49370dSWilliam A. Kennington III return bmcmetrics_metricproto_BmcFdStatMetric{ 312*7f49370dSWilliam A. Kennington III .stats = pbSubsEncoder< 313*7f49370dSWilliam A. Kennington III bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat_fields>(fds), 314*7f49370dSWilliam A. Kennington III }; 315*7f49370dSWilliam A. Kennington III } 316*7f49370dSWilliam A. Kennington III 317*7f49370dSWilliam A. Kennington III static bmcmetrics_metricproto_BmcMemoryMetric getMemMetric() noexcept 318*7f49370dSWilliam A. Kennington III { 319*7f49370dSWilliam A. Kennington III bmcmetrics_metricproto_BmcMemoryMetric ret = {}; 320*7f49370dSWilliam A. Kennington III auto data = readFileThenGrepIntoString("/proc/meminfo"); 321*7f49370dSWilliam A. Kennington III int value; 322*7f49370dSWilliam A. Kennington III if (parseMeminfoValue(data, "MemAvailable:", value)) 323*7f49370dSWilliam A. Kennington III { 324*7f49370dSWilliam A. Kennington III ret.mem_available = value; 325*7f49370dSWilliam A. Kennington III } 326*7f49370dSWilliam A. Kennington III if (parseMeminfoValue(data, "Slab:", value)) 327*7f49370dSWilliam A. Kennington III { 328*7f49370dSWilliam A. Kennington III ret.slab = value; 329*7f49370dSWilliam A. Kennington III } 330*7f49370dSWilliam A. Kennington III 331*7f49370dSWilliam A. Kennington III if (parseMeminfoValue(data, "KernelStack:", value)) 332*7f49370dSWilliam A. Kennington III { 333*7f49370dSWilliam A. Kennington III ret.kernel_stack = value; 334*7f49370dSWilliam A. Kennington III } 3351285115cSWilliam A. Kennington III return ret; 3361285115cSWilliam A. Kennington III } 3371285115cSWilliam A. Kennington III 338*7f49370dSWilliam A. Kennington III static bmcmetrics_metricproto_BmcUptimeMetric 339*7f49370dSWilliam A. Kennington III getUptimeMetric(bool& use) noexcept 3401285115cSWilliam A. Kennington III { 341*7f49370dSWilliam A. Kennington III bmcmetrics_metricproto_BmcUptimeMetric ret = {}; 3421285115cSWilliam A. Kennington III 343b63d6314SMichael Shen double uptime = 0; 344*7f49370dSWilliam A. Kennington III { 345*7f49370dSWilliam A. Kennington III auto data = readFileThenGrepIntoString("/proc/uptime"); 346b63d6314SMichael Shen double idleProcessTime = 0; 347*7f49370dSWilliam A. Kennington III if (!parseProcUptime(data, uptime, idleProcessTime)) 348b63d6314SMichael Shen { 349b63d6314SMichael Shen log<level::ERR>("Error parsing /proc/uptime"); 350*7f49370dSWilliam A. Kennington III return ret; 351b63d6314SMichael Shen } 352*7f49370dSWilliam A. Kennington III ret.uptime = uptime; 353*7f49370dSWilliam A. Kennington III ret.idle_process_time = idleProcessTime; 3541285115cSWilliam A. Kennington III } 3551285115cSWilliam A. Kennington III 356*7f49370dSWilliam A. Kennington III BootTimesMonotonic btm; 357*7f49370dSWilliam A. Kennington III if (!getBootTimesMonotonic(btm)) 358*7f49370dSWilliam A. Kennington III { 359*7f49370dSWilliam A. Kennington III log<level::ERR>("Could not get boot time"); 360*7f49370dSWilliam A. Kennington III return ret; 361*7f49370dSWilliam A. Kennington III } 362*7f49370dSWilliam A. Kennington III if (btm.firmwareTime == 0 && btm.powerOnSecCounterTime != 0) 363*7f49370dSWilliam A. Kennington III { 364*7f49370dSWilliam A. Kennington III ret.firmware_boot_time_sec = 365*7f49370dSWilliam A. Kennington III static_cast<double>(btm.powerOnSecCounterTime) - uptime; 366*7f49370dSWilliam A. Kennington III } 367*7f49370dSWilliam A. Kennington III else 368*7f49370dSWilliam A. Kennington III { 369*7f49370dSWilliam A. Kennington III ret.firmware_boot_time_sec = 370*7f49370dSWilliam A. Kennington III static_cast<double>(btm.firmwareTime - btm.loaderTime) / 1e6; 371*7f49370dSWilliam A. Kennington III } 372*7f49370dSWilliam A. Kennington III ret.loader_boot_time_sec = static_cast<double>(btm.loaderTime) / 1e6; 373*7f49370dSWilliam A. Kennington III if (btm.initrdTime != 0) 374*7f49370dSWilliam A. Kennington III { 375*7f49370dSWilliam A. Kennington III ret.kernel_boot_time_sec = static_cast<double>(btm.initrdTime) / 1e6; 376*7f49370dSWilliam A. Kennington III ret.initrd_boot_time_sec = 377*7f49370dSWilliam A. Kennington III static_cast<double>(btm.userspaceTime - btm.initrdTime) / 1e6; 378*7f49370dSWilliam A. Kennington III ret.userspace_boot_time_sec = 379*7f49370dSWilliam A. Kennington III static_cast<double>(btm.finishTime - btm.userspaceTime) / 1e6; 380*7f49370dSWilliam A. Kennington III } 381*7f49370dSWilliam A. Kennington III else 382*7f49370dSWilliam A. Kennington III { 383*7f49370dSWilliam A. Kennington III ret.kernel_boot_time_sec = static_cast<double>(btm.userspaceTime) / 1e6; 384*7f49370dSWilliam A. Kennington III ret.initrd_boot_time_sec = 0; 385*7f49370dSWilliam A. Kennington III ret.userspace_boot_time_sec = 386*7f49370dSWilliam A. Kennington III static_cast<double>(btm.finishTime - btm.userspaceTime) / 1e6; 387*7f49370dSWilliam A. Kennington III } 388*7f49370dSWilliam A. Kennington III 389*7f49370dSWilliam A. Kennington III use = true; 390*7f49370dSWilliam A. Kennington III return ret; 391*7f49370dSWilliam A. Kennington III } 392*7f49370dSWilliam A. Kennington III 393*7f49370dSWilliam A. Kennington III static bmcmetrics_metricproto_BmcDiskSpaceMetric 394*7f49370dSWilliam A. Kennington III getStorageMetric(bool& use) noexcept 395*7f49370dSWilliam A. Kennington III { 396*7f49370dSWilliam A. Kennington III bmcmetrics_metricproto_BmcDiskSpaceMetric ret = {}; 3971285115cSWilliam A. Kennington III struct statvfs fiData; 398*7f49370dSWilliam A. Kennington III if (statvfs("/", &fiData) < 0) 3991285115cSWilliam A. Kennington III { 4001285115cSWilliam A. Kennington III log<level::ERR>("Could not call statvfs"); 4011285115cSWilliam A. Kennington III } 4021285115cSWilliam A. Kennington III else 4031285115cSWilliam A. Kennington III { 404*7f49370dSWilliam A. Kennington III ret.rwfs_kib_available = (fiData.f_bsize * fiData.f_bfree) / 1024; 405*7f49370dSWilliam A. Kennington III use = true; 406*7f49370dSWilliam A. Kennington III } 407*7f49370dSWilliam A. Kennington III return ret; 4081285115cSWilliam A. Kennington III } 4091285115cSWilliam A. Kennington III 410*7f49370dSWilliam A. Kennington III void BmcHealthSnapshot::doWork() 411*7f49370dSWilliam A. Kennington III { 4121285115cSWilliam A. Kennington III // The next metrics require a sane ticks_per_sec value, typically 100 on 4131285115cSWilliam A. Kennington III // the BMC. In the very rare circumstance when it's 0, exit early and return 4141285115cSWilliam A. Kennington III // a partially complete snapshot (no process). 4151285115cSWilliam A. Kennington III ticksPerSec = getTicksPerSec(); 4161285115cSWilliam A. Kennington III 417*7f49370dSWilliam A. Kennington III static constexpr auto stcb = [](pb_ostream_t* stream, 418*7f49370dSWilliam A. Kennington III const pb_field_t* field, 419*7f49370dSWilliam A. Kennington III void* const* arg) noexcept { 420*7f49370dSWilliam A. Kennington III auto& self = *reinterpret_cast<BmcHealthSnapshot*>(*arg); 421*7f49370dSWilliam A. Kennington III std::vector<std::string_view> strs(self.stringTable.size()); 422*7f49370dSWilliam A. Kennington III for (const auto& [str, i] : self.stringTable) 4231285115cSWilliam A. Kennington III { 424*7f49370dSWilliam A. Kennington III strs[i] = str; 425*7f49370dSWilliam A. Kennington III } 426*7f49370dSWilliam A. Kennington III for (auto& str : strs) 427*7f49370dSWilliam A. Kennington III { 428*7f49370dSWilliam A. Kennington III bmcmetrics_metricproto_BmcStringTable_StringEntry msg = { 429*7f49370dSWilliam A. Kennington III .value = pbStrEncoder(str), 430*7f49370dSWilliam A. Kennington III }; 431*7f49370dSWilliam A. Kennington III if (!pb_encode_tag_for_field(stream, field) || 432*7f49370dSWilliam A. Kennington III !pb_encode_submessage( 433*7f49370dSWilliam A. Kennington III stream, 434*7f49370dSWilliam A. Kennington III bmcmetrics_metricproto_BmcStringTable_StringEntry_fields, 435*7f49370dSWilliam A. Kennington III &msg)) 436*7f49370dSWilliam A. Kennington III { 437*7f49370dSWilliam A. Kennington III return false; 438*7f49370dSWilliam A. Kennington III } 439*7f49370dSWilliam A. Kennington III } 440*7f49370dSWilliam A. Kennington III return true; 441*7f49370dSWilliam A. Kennington III }; 442*7f49370dSWilliam A. Kennington III std::vector<bmcmetrics_metricproto_BmcProcStatMetric_BmcProcStat> procs; 443*7f49370dSWilliam A. Kennington III std::vector<bmcmetrics_metricproto_BmcFdStatMetric_BmcFdStat> fds; 444*7f49370dSWilliam A. Kennington III bmcmetrics_metricproto_BmcMetricSnapshot snapshot = { 445*7f49370dSWilliam A. Kennington III .has_string_table = true, 446*7f49370dSWilliam A. Kennington III .string_table = 447*7f49370dSWilliam A. Kennington III { 448*7f49370dSWilliam A. Kennington III .entries = {{.encode = stcb}, this}, 449*7f49370dSWilliam A. Kennington III }, 450*7f49370dSWilliam A. Kennington III .has_memory_metric = true, 451*7f49370dSWilliam A. Kennington III .memory_metric = getMemMetric(), 452*7f49370dSWilliam A. Kennington III .has_uptime_metric = false, 453*7f49370dSWilliam A. Kennington III .uptime_metric = getUptimeMetric(snapshot.has_uptime_metric), 454*7f49370dSWilliam A. Kennington III .has_storage_space_metric = false, 455*7f49370dSWilliam A. Kennington III .storage_space_metric = 456*7f49370dSWilliam A. Kennington III getStorageMetric(snapshot.has_storage_space_metric), 457*7f49370dSWilliam A. Kennington III .has_procstat_metric = false, 458*7f49370dSWilliam A. Kennington III .procstat_metric = getProcStatMetric(*this, ticksPerSec, procs, 459*7f49370dSWilliam A. Kennington III snapshot.has_procstat_metric), 460*7f49370dSWilliam A. Kennington III .has_fdstat_metric = false, 461*7f49370dSWilliam A. Kennington III .fdstat_metric = getFdStatMetric(*this, ticksPerSec, fds, 462*7f49370dSWilliam A. Kennington III snapshot.has_fdstat_metric), 463*7f49370dSWilliam A. Kennington III }; 464*7f49370dSWilliam A. Kennington III pb_ostream_t nost = {}; 465*7f49370dSWilliam A. Kennington III if (!pb_encode(&nost, bmcmetrics_metricproto_BmcMetricSnapshot_fields, 466*7f49370dSWilliam A. Kennington III &snapshot)) 467*7f49370dSWilliam A. Kennington III { 468*7f49370dSWilliam A. Kennington III auto msg = std::format("Getting pb size: {}", PB_GET_ERROR(&nost)); 469*7f49370dSWilliam A. Kennington III log<level::ERR>(msg.c_str()); 4701285115cSWilliam A. Kennington III return; 4711285115cSWilliam A. Kennington III } 472*7f49370dSWilliam A. Kennington III pbDump.resize(nost.bytes_written); 473*7f49370dSWilliam A. Kennington III auto ost = pb_ostream_from_buffer( 474*7f49370dSWilliam A. Kennington III reinterpret_cast<pb_byte_t*>(pbDump.data()), pbDump.size()); 475*7f49370dSWilliam A. Kennington III if (!pb_encode(&ost, bmcmetrics_metricproto_BmcMetricSnapshot_fields, 476*7f49370dSWilliam A. Kennington III &snapshot)) 4771285115cSWilliam A. Kennington III { 478*7f49370dSWilliam A. Kennington III auto msg = std::format("Writing pb msg: {}", PB_GET_ERROR(&ost)); 479*7f49370dSWilliam A. Kennington III log<level::ERR>(msg.c_str()); 480*7f49370dSWilliam A. Kennington III return; 4811285115cSWilliam A. Kennington III } 4821285115cSWilliam A. Kennington III done = true; 4831285115cSWilliam A. Kennington III } 4841285115cSWilliam A. Kennington III 4851285115cSWilliam A. Kennington III // BmcBlobSessionStat (9) but passing meta as reference instead of pointer, 4861285115cSWilliam A. Kennington III // since the metadata must not be null at this point. 4871285115cSWilliam A. Kennington III bool BmcHealthSnapshot::stat(blobs::BlobMeta& meta) 4881285115cSWilliam A. Kennington III { 4891285115cSWilliam A. Kennington III if (!done) 4901285115cSWilliam A. Kennington III { 4911285115cSWilliam A. Kennington III // Bits 8~15 are blob-specific state flags. 4921285115cSWilliam A. Kennington III // For this blob, bit 8 is set when metric collection is still in 4931285115cSWilliam A. Kennington III // progress. 4941285115cSWilliam A. Kennington III meta.blobState |= (1 << 8); 4951285115cSWilliam A. Kennington III } 4961285115cSWilliam A. Kennington III else 4971285115cSWilliam A. Kennington III { 4981285115cSWilliam A. Kennington III meta.blobState = 0; 4991285115cSWilliam A. Kennington III meta.blobState = blobs::StateFlags::open_read; 5001285115cSWilliam A. Kennington III meta.size = pbDump.size(); 5011285115cSWilliam A. Kennington III } 5021285115cSWilliam A. Kennington III return true; 5031285115cSWilliam A. Kennington III } 5041285115cSWilliam A. Kennington III 5051285115cSWilliam A. Kennington III std::string_view BmcHealthSnapshot::read(uint32_t offset, 5061285115cSWilliam A. Kennington III uint32_t requestedSize) 5071285115cSWilliam A. Kennington III { 5081285115cSWilliam A. Kennington III uint32_t size = static_cast<uint32_t>(pbDump.size()); 5091285115cSWilliam A. Kennington III if (offset >= size) 5101285115cSWilliam A. Kennington III { 5111285115cSWilliam A. Kennington III return {}; 5121285115cSWilliam A. Kennington III } 5131285115cSWilliam A. Kennington III return std::string_view(pbDump.data() + offset, 5141285115cSWilliam A. Kennington III std::min(requestedSize, size - offset)); 5151285115cSWilliam A. Kennington III } 5161285115cSWilliam A. Kennington III 5171285115cSWilliam A. Kennington III int BmcHealthSnapshot::getStringID(const std::string_view s) 5181285115cSWilliam A. Kennington III { 5191285115cSWilliam A. Kennington III int ret = 0; 5201285115cSWilliam A. Kennington III auto itr = stringTable.find(s.data()); 5211285115cSWilliam A. Kennington III if (itr == stringTable.end()) 5221285115cSWilliam A. Kennington III { 5231285115cSWilliam A. Kennington III stringTable[s.data()] = stringId; 5241285115cSWilliam A. Kennington III ret = stringId; 5251285115cSWilliam A. Kennington III ++stringId; 5261285115cSWilliam A. Kennington III } 5271285115cSWilliam A. Kennington III else 5281285115cSWilliam A. Kennington III { 5291285115cSWilliam A. Kennington III ret = itr->second; 5301285115cSWilliam A. Kennington III } 5311285115cSWilliam A. Kennington III return ret; 5321285115cSWilliam A. Kennington III } 5331285115cSWilliam A. Kennington III 5341285115cSWilliam A. Kennington III } // namespace metric_blob 535