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