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 "util.hpp" 161285115cSWilliam A. Kennington III 17b63d6314SMichael Shen #include <fcntl.h> 18b63d6314SMichael Shen #include <sys/mman.h> 19b63d6314SMichael Shen #include <sys/stat.h> 201285115cSWilliam A. Kennington III #include <unistd.h> 211285115cSWilliam A. Kennington III 221285115cSWilliam A. Kennington III #include <phosphor-logging/log.hpp> 23b63d6314SMichael Shen #include <sdbusplus/bus.hpp> 24b63d6314SMichael Shen #include <sdbusplus/message.hpp> 251285115cSWilliam A. Kennington III 261285115cSWilliam A. Kennington III #include <cmath> 271285115cSWilliam A. Kennington III #include <cstdlib> 281285115cSWilliam A. Kennington III #include <fstream> 291285115cSWilliam A. Kennington III #include <sstream> 301285115cSWilliam A. Kennington III #include <string> 311285115cSWilliam A. Kennington III #include <string_view> 32b63d6314SMichael Shen #include <unordered_map> 33b63d6314SMichael Shen #include <utility> 34b63d6314SMichael Shen #include <variant> 35b63d6314SMichael Shen #include <vector> 361285115cSWilliam A. Kennington III 371285115cSWilliam A. Kennington III namespace metric_blob 381285115cSWilliam A. Kennington III { 391285115cSWilliam A. Kennington III 401285115cSWilliam A. Kennington III using phosphor::logging::log; 411285115cSWilliam A. Kennington III using level = phosphor::logging::level; 421285115cSWilliam A. Kennington III 431285115cSWilliam A. Kennington III char controlCharsToSpace(char c) 441285115cSWilliam A. Kennington III { 451285115cSWilliam A. Kennington III if (c < 32) 461285115cSWilliam A. Kennington III { 471285115cSWilliam A. Kennington III c = ' '; 481285115cSWilliam A. Kennington III } 491285115cSWilliam A. Kennington III return c; 501285115cSWilliam A. Kennington III } 511285115cSWilliam A. Kennington III 521285115cSWilliam A. Kennington III long getTicksPerSec() 531285115cSWilliam A. Kennington III { 541285115cSWilliam A. Kennington III return sysconf(_SC_CLK_TCK); 551285115cSWilliam A. Kennington III } 561285115cSWilliam A. Kennington III 57b63d6314SMichael Shen std::string readFileThenGrepIntoString(const std::string_view fileName, 58b63d6314SMichael Shen const std::string_view grepStr) 591285115cSWilliam A. Kennington III { 601285115cSWilliam A. Kennington III std::stringstream ss; 611285115cSWilliam A. Kennington III std::ifstream ifs(fileName.data()); 621285115cSWilliam A. Kennington III while (ifs.good()) 631285115cSWilliam A. Kennington III { 641285115cSWilliam A. Kennington III std::string line; 651285115cSWilliam A. Kennington III std::getline(ifs, line); 66b63d6314SMichael Shen if (line.find(grepStr) != std::string::npos) 67b63d6314SMichael Shen { 681285115cSWilliam A. Kennington III ss << line; 69b63d6314SMichael Shen } 701285115cSWilliam A. Kennington III if (ifs.good()) 711285115cSWilliam A. Kennington III ss << std::endl; 721285115cSWilliam A. Kennington III } 731285115cSWilliam A. Kennington III return ss.str(); 741285115cSWilliam A. Kennington III } 751285115cSWilliam A. Kennington III 761285115cSWilliam A. Kennington III bool isNumericPath(const std::string_view path, int& value) 771285115cSWilliam A. Kennington III { 781285115cSWilliam A. Kennington III size_t p = path.rfind('/'); 791285115cSWilliam A. Kennington III if (p == std::string::npos) 801285115cSWilliam A. Kennington III { 811285115cSWilliam A. Kennington III return false; 821285115cSWilliam A. Kennington III } 831285115cSWilliam A. Kennington III int id = 0; 841285115cSWilliam A. Kennington III for (size_t i = p + 1; i < path.size(); ++i) 851285115cSWilliam A. Kennington III { 861285115cSWilliam A. Kennington III const char ch = path[i]; 871285115cSWilliam A. Kennington III if (ch < '0' || ch > '9') 881285115cSWilliam A. Kennington III return false; 891285115cSWilliam A. Kennington III else 901285115cSWilliam A. Kennington III { 911285115cSWilliam A. Kennington III id = id * 10 + (ch - '0'); 921285115cSWilliam A. Kennington III } 931285115cSWilliam A. Kennington III } 941285115cSWilliam A. Kennington III value = id; 951285115cSWilliam A. Kennington III return true; 961285115cSWilliam A. Kennington III } 971285115cSWilliam A. Kennington III 981285115cSWilliam A. Kennington III // Trims all control characters at the end of a string. 991285115cSWilliam A. Kennington III std::string trimStringRight(std::string_view s) 1001285115cSWilliam A. Kennington III { 1011285115cSWilliam A. Kennington III std::string ret(s.data()); 1021285115cSWilliam A. Kennington III while (!ret.empty()) 1031285115cSWilliam A. Kennington III { 1041285115cSWilliam A. Kennington III if (ret.back() <= 32) 1051285115cSWilliam A. Kennington III ret.pop_back(); 1061285115cSWilliam A. Kennington III else 1071285115cSWilliam A. Kennington III break; 1081285115cSWilliam A. Kennington III } 1091285115cSWilliam A. Kennington III return ret; 1101285115cSWilliam A. Kennington III } 1111285115cSWilliam A. Kennington III 1121285115cSWilliam A. Kennington III std::string getCmdLine(const int pid) 1131285115cSWilliam A. Kennington III { 114*c66ebc35SPatrick Williams const std::string& cmdlinePath = 115*c66ebc35SPatrick Williams "/proc/" + std::to_string(pid) + "/cmdline"; 1161285115cSWilliam A. Kennington III 117b63d6314SMichael Shen std::string cmdline = readFileThenGrepIntoString(cmdlinePath); 1181285115cSWilliam A. Kennington III for (size_t i = 0; i < cmdline.size(); ++i) 1191285115cSWilliam A. Kennington III { 1201285115cSWilliam A. Kennington III cmdline[i] = controlCharsToSpace(cmdline[i]); 1211285115cSWilliam A. Kennington III } 1221285115cSWilliam A. Kennington III 1231285115cSWilliam A. Kennington III // Trim empty strings 1241285115cSWilliam A. Kennington III cmdline = trimStringRight(cmdline); 1251285115cSWilliam A. Kennington III 1261285115cSWilliam A. Kennington III return cmdline; 1271285115cSWilliam A. Kennington III } 1281285115cSWilliam A. Kennington III 1291285115cSWilliam A. Kennington III // strtok is used in this function in order to avoid usage of <sstream>. 1301285115cSWilliam A. Kennington III // However, that would require us to create a temporary std::string. 1311285115cSWilliam A. Kennington III TcommUtimeStime parseTcommUtimeStimeString(std::string_view content, 1321285115cSWilliam A. Kennington III const long ticksPerSec) 1331285115cSWilliam A. Kennington III { 1341285115cSWilliam A. Kennington III TcommUtimeStime ret; 1351285115cSWilliam A. Kennington III ret.tcomm = ""; 1361285115cSWilliam A. Kennington III ret.utime = ret.stime = 0; 1371285115cSWilliam A. Kennington III 1381285115cSWilliam A. Kennington III const float invTicksPerSec = 1.0f / static_cast<float>(ticksPerSec); 1391285115cSWilliam A. Kennington III 1401285115cSWilliam A. Kennington III // pCol now points to the first part in content after content is split by 1411285115cSWilliam A. Kennington III // space. 1421285115cSWilliam A. Kennington III // This is not ideal, 1431285115cSWilliam A. Kennington III std::string temp(content); 1441285115cSWilliam A. Kennington III char* pCol = strtok(temp.data(), " "); 1451285115cSWilliam A. Kennington III 1461285115cSWilliam A. Kennington III if (pCol != nullptr) 1471285115cSWilliam A. Kennington III { 1481285115cSWilliam A. Kennington III const int fields[] = {1, 13, 14}; // tcomm, utime, stime 1491285115cSWilliam A. Kennington III int fieldIdx = 0; 1501285115cSWilliam A. Kennington III for (int colIdx = 0; colIdx < 15; ++colIdx) 1511285115cSWilliam A. Kennington III { 1521285115cSWilliam A. Kennington III if (fieldIdx < 3 && colIdx == fields[fieldIdx]) 1531285115cSWilliam A. Kennington III { 1541285115cSWilliam A. Kennington III switch (fieldIdx) 1551285115cSWilliam A. Kennington III { 1561285115cSWilliam A. Kennington III case 0: 1571285115cSWilliam A. Kennington III { 1581285115cSWilliam A. Kennington III ret.tcomm = std::string(pCol); 1591285115cSWilliam A. Kennington III break; 1601285115cSWilliam A. Kennington III } 1611285115cSWilliam A. Kennington III case 1: 1621285115cSWilliam A. Kennington III [[fallthrough]]; 1631285115cSWilliam A. Kennington III case 2: 1641285115cSWilliam A. Kennington III { 1651285115cSWilliam A. Kennington III int ticks = std::atoi(pCol); 1661285115cSWilliam A. Kennington III float t = static_cast<float>(ticks) * invTicksPerSec; 1671285115cSWilliam A. Kennington III 1681285115cSWilliam A. Kennington III if (fieldIdx == 1) 1691285115cSWilliam A. Kennington III { 1701285115cSWilliam A. Kennington III ret.utime = t; 1711285115cSWilliam A. Kennington III } 1721285115cSWilliam A. Kennington III else if (fieldIdx == 2) 1731285115cSWilliam A. Kennington III { 1741285115cSWilliam A. Kennington III ret.stime = t; 1751285115cSWilliam A. Kennington III } 1761285115cSWilliam A. Kennington III break; 1771285115cSWilliam A. Kennington III } 1781285115cSWilliam A. Kennington III } 1791285115cSWilliam A. Kennington III ++fieldIdx; 1801285115cSWilliam A. Kennington III } 1811285115cSWilliam A. Kennington III pCol = strtok(nullptr, " "); 1821285115cSWilliam A. Kennington III } 1831285115cSWilliam A. Kennington III } 1841285115cSWilliam A. Kennington III 1851285115cSWilliam A. Kennington III if (ticksPerSec <= 0) 1861285115cSWilliam A. Kennington III { 1871285115cSWilliam A. Kennington III log<level::ERR>("ticksPerSec is equal or less than zero"); 1881285115cSWilliam A. Kennington III } 1891285115cSWilliam A. Kennington III 1901285115cSWilliam A. Kennington III return ret; 1911285115cSWilliam A. Kennington III } 1921285115cSWilliam A. Kennington III 1931285115cSWilliam A. Kennington III TcommUtimeStime getTcommUtimeStime(const int pid, const long ticksPerSec) 1941285115cSWilliam A. Kennington III { 1951285115cSWilliam A. Kennington III const std::string& statPath = "/proc/" + std::to_string(pid) + "/stat"; 196b63d6314SMichael Shen return parseTcommUtimeStimeString(readFileThenGrepIntoString(statPath), 1971285115cSWilliam A. Kennington III ticksPerSec); 1981285115cSWilliam A. Kennington III } 1991285115cSWilliam A. Kennington III 2001285115cSWilliam A. Kennington III // Returns true if successfully parsed and false otherwise. If parsing was 2011285115cSWilliam A. Kennington III // successful, value is set accordingly. 2021285115cSWilliam A. Kennington III // Input: "MemAvailable: 1234 kB" 2031285115cSWilliam A. Kennington III // Returns true, value set to 1234 2041285115cSWilliam A. Kennington III bool parseMeminfoValue(const std::string_view content, 2051285115cSWilliam A. Kennington III const std::string_view keyword, int& value) 2061285115cSWilliam A. Kennington III { 2071285115cSWilliam A. Kennington III size_t p = content.find(keyword); 2081285115cSWilliam A. Kennington III if (p != std::string::npos) 2091285115cSWilliam A. Kennington III { 2101285115cSWilliam A. Kennington III std::string_view v = content.substr(p + keyword.size()); 2111285115cSWilliam A. Kennington III p = v.find("kB"); 2121285115cSWilliam A. Kennington III if (p != std::string::npos) 2131285115cSWilliam A. Kennington III { 2141285115cSWilliam A. Kennington III v = v.substr(0, p); 2151285115cSWilliam A. Kennington III value = std::atoi(v.data()); 2161285115cSWilliam A. Kennington III return true; 2171285115cSWilliam A. Kennington III } 2181285115cSWilliam A. Kennington III } 2191285115cSWilliam A. Kennington III return false; 2201285115cSWilliam A. Kennington III } 2211285115cSWilliam A. Kennington III 2221285115cSWilliam A. Kennington III bool parseProcUptime(const std::string_view content, double& uptime, 2231285115cSWilliam A. Kennington III double& idleProcessTime) 2241285115cSWilliam A. Kennington III { 2251285115cSWilliam A. Kennington III double t0, t1; // Attempts to parse uptime and idleProcessTime 2261285115cSWilliam A. Kennington III int ret = sscanf(content.data(), "%lf %lf", &t0, &t1); 2271285115cSWilliam A. Kennington III if (ret == 2 && std::isfinite(t0) && std::isfinite(t1)) 2281285115cSWilliam A. Kennington III { 2291285115cSWilliam A. Kennington III uptime = t0; 2301285115cSWilliam A. Kennington III idleProcessTime = t1; 2311285115cSWilliam A. Kennington III return true; 2321285115cSWilliam A. Kennington III } 2331285115cSWilliam A. Kennington III return false; 2341285115cSWilliam A. Kennington III } 2351285115cSWilliam A. Kennington III 236b63d6314SMichael Shen bool readMem(const uint32_t target, uint32_t& memResult) 237b63d6314SMichael Shen { 238b63d6314SMichael Shen int fd = open("/dev/mem", O_RDONLY | O_SYNC); 239b63d6314SMichael Shen if (fd < 0) 240b63d6314SMichael Shen { 241b63d6314SMichael Shen return false; 242b63d6314SMichael Shen } 243b63d6314SMichael Shen 244b63d6314SMichael Shen int pageSize = getpagesize(); 245b63d6314SMichael Shen uint32_t pageOffset = target & ~static_cast<uint32_t>(pageSize - 1); 246b63d6314SMichael Shen uint32_t offsetInPage = target & static_cast<uint32_t>(pageSize - 1); 247b63d6314SMichael Shen 248*c66ebc35SPatrick Williams void* mapBase = 249*c66ebc35SPatrick Williams mmap(NULL, pageSize * 2, PROT_READ, MAP_SHARED, fd, pageOffset); 250b63d6314SMichael Shen if (mapBase == MAP_FAILED) 251b63d6314SMichael Shen { 252b63d6314SMichael Shen close(fd); 253b63d6314SMichael Shen return false; 254b63d6314SMichael Shen } 255b63d6314SMichael Shen 256b63d6314SMichael Shen char* virtAddr = reinterpret_cast<char*>(mapBase) + offsetInPage; 257b63d6314SMichael Shen memResult = *(reinterpret_cast<uint32_t*>(virtAddr)); 258b63d6314SMichael Shen close(fd); 2593d7cd157SMichael Shen munmap(mapBase, pageSize * 2); 260b63d6314SMichael Shen return true; 261b63d6314SMichael Shen } 262b63d6314SMichael Shen 263b63d6314SMichael Shen // clang-format off 264b63d6314SMichael Shen /* 265b63d6314SMichael Shen * power-on 266b63d6314SMichael Shen * counter(start) uptime(start) 267b63d6314SMichael Shen * firmware(Neg) loader(Neg) kernel(always 0) initrd userspace finish 268b63d6314SMichael Shen * |----------------|-------------|-------------------|----------------------|----------------------| 269b63d6314SMichael Shen * |----------------| <--- firmwareTime=firmware-loader 270b63d6314SMichael Shen * |-------------| <--- loaderTime=loader 271b63d6314SMichael Shen * |------------------------------| <--- firmwareTime(Actually is firmware+loader)=counter-uptime \ 272b63d6314SMichael Shen * (in this case we can treat this as firmware time \ 273b63d6314SMichael Shen * since firmware consumes most of the time) 274b63d6314SMichael Shen * |-------------------| <--- kernelTime=initrd (if initrd present) 275b63d6314SMichael Shen * |------------------------------------------| <--- kernelTime=userspace (if no initrd) 276b63d6314SMichael Shen * |----------------------| <--- initrdTime=userspace-initrd (if initrd present) 277b63d6314SMichael Shen * |----------------------| <--- userspaceTime=finish-userspace 278b63d6314SMichael Shen */ 279b63d6314SMichael Shen // clang-format on 280b63d6314SMichael Shen bool getBootTimesMonotonic(BootTimesMonotonic& btm) 281b63d6314SMichael Shen { 282b63d6314SMichael Shen // Timestamp name and its offset in the struct. 283b63d6314SMichael Shen std::vector<std::pair<std::string_view, size_t>> timeMap = { 284b63d6314SMichael Shen {"FirmwareTimestampMonotonic", 285b63d6314SMichael Shen offsetof(BootTimesMonotonic, firmwareTime)}, // negative value 286b63d6314SMichael Shen {"LoaderTimestampMonotonic", 287b63d6314SMichael Shen offsetof(BootTimesMonotonic, loaderTime)}, // negative value 288b63d6314SMichael Shen {"InitRDTimestampMonotonic", offsetof(BootTimesMonotonic, initrdTime)}, 289b63d6314SMichael Shen {"UserspaceTimestampMonotonic", 290b63d6314SMichael Shen offsetof(BootTimesMonotonic, userspaceTime)}, 291b63d6314SMichael Shen {"FinishTimestampMonotonic", offsetof(BootTimesMonotonic, finishTime)}}; 292b63d6314SMichael Shen 293b63d6314SMichael Shen auto b = sdbusplus::bus::new_default_system(); 294b63d6314SMichael Shen auto m = b.new_method_call("org.freedesktop.systemd1", 295b63d6314SMichael Shen "/org/freedesktop/systemd1", 296b63d6314SMichael Shen "org.freedesktop.DBus.Properties", "GetAll"); 297b63d6314SMichael Shen m.append(""); 298b63d6314SMichael Shen auto reply = b.call(m); 299b63d6314SMichael Shen std::vector<std::pair<std::string, std::variant<uint64_t>>> timestamps; 300b63d6314SMichael Shen reply.read(timestamps); 301b63d6314SMichael Shen 302b63d6314SMichael Shen // Parse timestamps from dbus result. 303b63d6314SMichael Shen auto btmPtr = reinterpret_cast<char*>(&btm); 304b63d6314SMichael Shen unsigned int recordCnt = 0; 305b63d6314SMichael Shen for (auto& t : timestamps) 306b63d6314SMichael Shen { 307b63d6314SMichael Shen for (auto& tm : timeMap) 308b63d6314SMichael Shen { 309b63d6314SMichael Shen if (tm.first.compare(t.first) == 0) 310b63d6314SMichael Shen { 311b63d6314SMichael Shen auto temp = std::get<uint64_t>(t.second); 312b63d6314SMichael Shen memcpy(btmPtr + tm.second, reinterpret_cast<char*>(&temp), 313b63d6314SMichael Shen sizeof(temp)); 314b63d6314SMichael Shen recordCnt++; 315b63d6314SMichael Shen break; 316b63d6314SMichael Shen } 317b63d6314SMichael Shen } 318b63d6314SMichael Shen if (recordCnt == timeMap.size()) 319b63d6314SMichael Shen { 320b63d6314SMichael Shen break; 321b63d6314SMichael Shen } 322b63d6314SMichael Shen } 323b63d6314SMichael Shen if (recordCnt != timeMap.size()) 324b63d6314SMichael Shen { 325b63d6314SMichael Shen log<level::ERR>("Didn't get desired timestamps"); 326b63d6314SMichael Shen return false; 327b63d6314SMichael Shen } 328b63d6314SMichael Shen 329*c66ebc35SPatrick Williams std::string cpuinfo = 330*c66ebc35SPatrick Williams readFileThenGrepIntoString("/proc/cpuinfo", "Hardware"); 331b63d6314SMichael Shen // Nuvoton NPCM7XX chip has a counter which starts from power-on. 332b63d6314SMichael Shen if (cpuinfo.find("NPCM7XX") != std::string::npos) 333b63d6314SMichael Shen { 334b63d6314SMichael Shen // Get elapsed seconds from SEC_CNT register 335b63d6314SMichael Shen const uint32_t SEC_CNT_ADDR = 0xf0801068; 336b63d6314SMichael Shen uint32_t memResult = 0; 337b63d6314SMichael Shen if (readMem(SEC_CNT_ADDR, memResult)) 338b63d6314SMichael Shen { 339b63d6314SMichael Shen btm.powerOnSecCounterTime = static_cast<uint64_t>(memResult); 340b63d6314SMichael Shen } 341b63d6314SMichael Shen else 342b63d6314SMichael Shen { 343b63d6314SMichael Shen log<level::ERR>("Read memory SEC_CNT_ADDR(0xf0801068) failed"); 344b63d6314SMichael Shen return false; 345b63d6314SMichael Shen } 346b63d6314SMichael Shen } 347b63d6314SMichael Shen 348b63d6314SMichael Shen return true; 349b63d6314SMichael Shen } 350b63d6314SMichael Shen 3514dba220dSWilly Tu bool getECCErrorCounts(EccCounts& eccCounts) 3524dba220dSWilly Tu { 3534dba220dSWilly Tu std::vector< 3544dba220dSWilly Tu std::pair<std::string, std::variant<uint64_t, uint8_t, std::string>>> 3554dba220dSWilly Tu values; 3564dba220dSWilly Tu try 3574dba220dSWilly Tu { 3584dba220dSWilly Tu auto bus = sdbusplus::bus::new_default_system(); 3594dba220dSWilly Tu auto m = 3604dba220dSWilly Tu bus.new_method_call("xyz.openbmc_project.memory.ECC", 3614dba220dSWilly Tu "/xyz/openbmc_project/metrics/memory/BmcECC", 3624dba220dSWilly Tu "org.freedesktop.DBus.Properties", "GetAll"); 3634dba220dSWilly Tu m.append("xyz.openbmc_project.Memory.MemoryECC"); 3644dba220dSWilly Tu auto reply = bus.call(m); 3654dba220dSWilly Tu reply.read(values); 3664dba220dSWilly Tu } 3674dba220dSWilly Tu catch (const sdbusplus::exception::SdBusError& ex) 3684dba220dSWilly Tu { 3694dba220dSWilly Tu return false; 3704dba220dSWilly Tu } 3714dba220dSWilly Tu bool hasCorrectable = false; 3724dba220dSWilly Tu bool hasUncorrectable = false; 3734dba220dSWilly Tu for (const auto& [key, value] : values) 3744dba220dSWilly Tu { 3754dba220dSWilly Tu if (key == "ceCount") 3764dba220dSWilly Tu { 3774dba220dSWilly Tu eccCounts.correctableErrCount = 3784dba220dSWilly Tu static_cast<int32_t>(std::get<uint64_t>(value)); 3794dba220dSWilly Tu hasCorrectable = true; 3804dba220dSWilly Tu if (hasUncorrectable) 3814dba220dSWilly Tu { 3824dba220dSWilly Tu return true; 3834dba220dSWilly Tu } 3844dba220dSWilly Tu } 3854dba220dSWilly Tu if (key == "ueCount") 3864dba220dSWilly Tu { 3874dba220dSWilly Tu eccCounts.uncorrectableErrCount = 3884dba220dSWilly Tu static_cast<int32_t>(std::get<uint64_t>(value)); 3894dba220dSWilly Tu hasUncorrectable = true; 3904dba220dSWilly Tu if (hasCorrectable) 3914dba220dSWilly Tu { 3924dba220dSWilly Tu return true; 3934dba220dSWilly Tu } 3944dba220dSWilly Tu } 3954dba220dSWilly Tu } 3964dba220dSWilly Tu return false; 3974dba220dSWilly Tu } 3984dba220dSWilly Tu 3991285115cSWilliam A. Kennington III } // namespace metric_blob 400