From 61eb18dc3dfa78a3501e0ea27fc9168a254196e7 Mon Sep 17 00:00:00 2001 From: Gabriel Russell Date: Wed, 26 Aug 2020 11:42:31 -0400 Subject: SERVER-50123 Record number of physical cores on all platforms --- src/mongo/util/processinfo.h | 11 +- src/mongo/util/processinfo_linux.cpp | 185 +++++++++++++++++++++++++++++---- src/mongo/util/processinfo_windows.cpp | 102 +++++++++++------- 3 files changed, 238 insertions(+), 60 deletions(-) diff --git a/src/mongo/util/processinfo.h b/src/mongo/util/processinfo.h index 408e633bd9b..4752b6d2e62 100644 --- a/src/mongo/util/processinfo.h +++ b/src/mongo/util/processinfo.h @@ -101,12 +101,19 @@ public: } /** - * Get the number of CPUs + * Get the number of (logical) CPUs */ static unsigned getNumCores() { return sysInfo().numCores; } + /** + * Get the number of physical CPUs + */ + static unsigned getNumPhysicalCores() { + return sysInfo().numPhysicalCores; + } + /** * Get the number of cores available. Make a best effort to get the cores for this process. * If that information is not available, get the total number of CPUs. @@ -208,6 +215,7 @@ private: unsigned long long memSize; unsigned long long memLimit; unsigned numCores; + unsigned numPhysicalCores; unsigned long long pageSize; std::string cpuArch; bool hasNuma; @@ -230,6 +238,7 @@ private: memSize(0), memLimit(0), numCores(0), + numPhysicalCores(0), pageSize(0), hasNuma(false), fileZeroNeeded(false), diff --git a/src/mongo/util/processinfo_linux.cpp b/src/mongo/util/processinfo_linux.cpp index 48acd9e3388..063d902fadc 100644 --- a/src/mongo/util/processinfo_linux.cpp +++ b/src/mongo/util/processinfo_linux.cpp @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -54,6 +55,7 @@ #include #include #include +#include #include "mongo/util/file.h" #include "mongo/util/log.h" @@ -70,7 +72,6 @@ public: LinuxProc(ProcessId pid) { char name[128]; sprintf(name, "/proc/%d/stat", pid.asUInt32()); - FILE* f = fopen(name, "r"); if (!f) { stringstream ss; @@ -137,7 +138,7 @@ public: */ ); if (found == 0) { - cout << "system error: reading proc info" << endl; + log() << "system error: reading proc info"; } fclose(f); } @@ -247,6 +248,112 @@ public: // The current EIP (instruction pointer). }; +namespace { + +// As described in the /proc/[pid]/mountinfo section of `man 5 proc`: +// +// 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue +// | | | | | | | | | | +// (1)(2)(3:4)(5) (6) (7) (8) (9) (10) (11) +struct MountRecord { + bool parseLine(const std::string& line) { + static const pcrecpp::RE kRe{ + // (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) + R"re((\d+) (\d+) (\d+):(\d+) (\S+) (\S+) (\S+) ((?:\S+:\S+ ?)*) - (\S+) (\S+) (\S+))re"}; + return kRe.FullMatch(line, + &mountId, + &parentId, + &major, + &minor, + &root, + &mountPoint, + &options, + &fields, + &type, + &source, + &superOpt); + } + + void appendBSON(BSONObjBuilder& bob) const { + bob.append("mountId", mountId) + .append("parentId", parentId) + .append("major", major) + .append("minor", minor) + .append("root", root) + .append("mountPoint", mountPoint) + .append("options", options) + .append("fields", fields) + .append("type", type) + .append("source", source) + .append("superOpt", superOpt); + } + + int mountId; // (1) unique ID for the mount + int parentId; // (2) the ID of the parent mount (self for the root mount) + int major; // (3) major block device number (see stat(2)) + int minor; // (4) minor block device number + std::string root; // (5) path in filesystem forming the root + std::string mountPoint; // (6) the mount point relative to the process's root + std::string options; // (7) per-mount options (see mount(2)). + std::string fields; // (8) zero or more: "tag[:value]" fields + std::string type; // (9) filesystem type: "type[.subtype]" + std::string source; // (10) fs-specific information or "none" + std::string superOpt; // (11) per-superblock options (see mount(2)) +}; + +void appendMountInfo(BSONObjBuilder& bob) { + std::ifstream ifs("/proc/self/mountinfo"); + if (!ifs) + return; + BSONArrayBuilder arr = bob.subarrayStart("mountInfo"); + std::string line; + MountRecord rec; + while (ifs && getline(ifs, line)) { + if (rec.parseLine(line)) { + auto bob = BSONObjBuilder(arr.subobjStart()); + rec.appendBSON(bob); + } + } +} + +class CpuInfoParser { +public: + struct LineProcessor { + pcrecpp::RE regex; + std::function f; + }; + std::vector lineProcessors; + std::function recordProcessor; + void run() { + std::ifstream f("/proc/cpuinfo"); + if (!f) + return; + + bool readSuccess; + bool unprocessed = false; + static const pcrecpp::RE lineRegex(R"re((.*?)\s*:\s*(.*))re"); + do { + std::string fstr; + readSuccess = f && std::getline(f, fstr); + if (readSuccess && !fstr.empty()) { + std::string key; + std::string value; + if (!lineRegex.FullMatch(fstr, &key, &value)) + continue; + for (auto&& lp : lineProcessors) { + if (lp.regex.FullMatch(key)) + lp.f(value); + } + unprocessed = true; + } else if (unprocessed) { + recordProcessor(); + unprocessed = false; + } + } while (readSuccess); + } +}; + +} // namespace class LinuxSysHelper { public: @@ -266,30 +373,63 @@ public: return fstr; } + + /** + * count the number of physical cores + */ + static void getNumPhysicalCores(int& physicalCores) { + + /* In /proc/cpuinfo core ids are only unique within a particular physical unit, AKA a cpu + * package, so to count the total cores we need to count the unique pairs of core id and + * physical id*/ + struct CpuId { + std::string core; + std::string physical; + }; + + CpuId parsedCpuId; + + auto cmp = [](auto&& a, auto&& b) { + auto tupLens = [](auto&& o) { return std::tie(o.core, o.physical); }; + return tupLens(a) < tupLens(b); + }; + std::set cpuIds(cmp); + + CpuInfoParser cpuInfoParser{ + { + {"physical id", [&](const std::string& value) { parsedCpuId.physical = value; }}, + {"core id", [&](const std::string& value) { parsedCpuId.core = value; }}, + }, + [&]() { + cpuIds.insert(parsedCpuId); + parsedCpuId = CpuId{}; + }}; + cpuInfoParser.run(); + + physicalCores = cpuIds.size(); + } + /** * Get some details about the CPU */ - static void getCpuInfo(int& procCount, string& freq, string& features) { - FILE* f; - char fstr[1024] = {0}; - procCount = 0; - - f = fopen("/proc/cpuinfo", "r"); - if (f == NULL) - return; + static void getCpuInfo(int& procCount, std::string& freq, std::string& features) { - while (fgets(fstr, 1023, f) != NULL && !feof(f)) { - // until the end of the file - fstr[strlen(fstr) < 1 ? 0 : strlen(fstr) - 1] = '\0'; - if (strncmp(fstr, "processor ", 10) == 0 || strncmp(fstr, "processor\t:", 11) == 0) - ++procCount; - if (strncmp(fstr, "cpu MHz\t\t:", 10) == 0) - freq = fstr + 11; - if (strncmp(fstr, "flags\t\t:", 8) == 0) - features = fstr + 9; - } + procCount = 0; - fclose(f); + CpuInfoParser cpuInfoParser{ + { +#ifdef __s390x__ + {R"re(processor\s+\d+)re", [&](const std::string& value) { procCount++; }}, + {"cpu MHz static", [&](const std::string& value) { freq = value; }}, + {"features", [&](const std::string& value) { features = value; }}, +#else + {"processor", [&](const std::string& value) { procCount++; }}, + {"cpu MHz", [&](const std::string& value) { freq = value; }}, + {"flags", [&](const std::string& value) { features = value; }}, +#endif + }, + []() {}}; + cpuInfoParser.run(); } /** @@ -492,9 +632,11 @@ void ProcessInfo::SystemInfo::collectSystemInfo() { string distroName, distroVersion; string cpuFreq, cpuFeatures; int cpuCount; + int physicalCores; string verSig = LinuxSysHelper::readLineFromFile("/proc/version_signature"); LinuxSysHelper::getCpuInfo(cpuCount, cpuFreq, cpuFeatures); + LinuxSysHelper::getNumPhysicalCores(physicalCores); LinuxSysHelper::getLinuxDistro(distroName, distroVersion); if (uname(&unameData) == -1) { @@ -531,6 +673,7 @@ void ProcessInfo::SystemInfo::collectSystemInfo() { bExtra.append("pageSize", static_cast(pageSize)); bExtra.append("numPages", static_cast(sysconf(_SC_PHYS_PAGES))); bExtra.append("maxOpenFiles", static_cast(sysconf(_SC_OPEN_MAX))); + bExtra.append("physicalCores", physicalCores); _extraStats = bExtra.obj(); } diff --git a/src/mongo/util/processinfo_windows.cpp b/src/mongo/util/processinfo_windows.cpp index 02dd7d4fa86..2e1ed74f923 100644 --- a/src/mongo/util/processinfo_windows.cpp +++ b/src/mongo/util/processinfo_windows.cpp @@ -71,6 +71,64 @@ struct PsApiInit { static PsApiInit* psapiGlobal = NULL; +namespace { + +using Slpi = SYSTEM_LOGICAL_PROCESSOR_INFORMATION; +using SlpiBuf = std::aligned_storage_t; + +struct LpiRecords { + const Slpi* begin() const { + return reinterpret_cast(slpiRecords.get()); + } + + const Slpi* end() const { + return begin() + count; + } + + std::unique_ptr slpiRecords; + size_t count; +}; + +// Both the body of this getLogicalProcessorInformationRecords and the callers of +// getLogicalProcessorInformationRecords are largely modeled off of the example code at +// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformation +LpiRecords getLogicalProcessorInformationRecords() { + + DWORD returnLength = 0; + LpiRecords lpiRecords{}; + + DWORD returnCode = 0; + do { + returnCode = GetLogicalProcessorInformation( + reinterpret_cast(lpiRecords.slpiRecords.get()), &returnLength); + if (returnCode == FALSE) { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + lpiRecords.slpiRecords = std::unique_ptr( + new SlpiBuf[((returnLength - 1) / sizeof(Slpi)) + 1]); + } else { + DWORD gle = GetLastError(); + warning() << "GetLogicalProcessorInformation failed" << errnoWithDescription(gle); + return LpiRecords{}; + } + } + } while (returnCode == FALSE); + + + lpiRecords.count = returnLength / sizeof(Slpi); + return lpiRecords; +} + +int getPhysicalCores() { + int processorCoreCount = 0; + for (auto&& lpi : getLogicalProcessorInformationRecords()) { + if (lpi.Relationship == RelationProcessorCore) + processorCoreCount++; + } + return processorCoreCount; +} + +} // namespace + int _wconvertmtos(SIZE_T s) { return (int)(s / (1024 * 1024)); } @@ -282,8 +340,10 @@ void ProcessInfo::SystemInfo::collectSystemInfo() { GetNativeSystemInfo(&ntsysinfo); addrSize = (ntsysinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? 64 : 32); numCores = ntsysinfo.dwNumberOfProcessors; + numPhysicalCores = getPhysicalCores(); pageSize = static_cast(ntsysinfo.dwPageSize); bExtra.append("pageSize", static_cast(pageSize)); + bExtra.append("physicalCores", static_cast(numPhysicalCores)); // get memory info mse.dwLength = sizeof(mse); @@ -387,47 +447,13 @@ void ProcessInfo::SystemInfo::collectSystemInfo() { } } -bool ProcessInfo::checkNumaEnabled() { - typedef BOOL(WINAPI * LPFN_GLPI)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); - DWORD returnLength = 0; +bool ProcessInfo::checkNumaEnabled() { DWORD numaNodeCount = 0; - unique_ptr buffer; - - LPFN_GLPI glpi(reinterpret_cast( - GetProcAddress(GetModuleHandleW(L"kernel32"), "GetLogicalProcessorInformation"))); - if (glpi == NULL) { - return false; - } - - DWORD returnCode = 0; - do { - returnCode = glpi(buffer.get(), &returnLength); - - if (returnCode == FALSE) { - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - buffer.reset(reinterpret_cast( - new BYTE[returnLength])); - } else { - DWORD gle = GetLastError(); - warning() << "GetLogicalProcessorInformation failed with " - << errnoWithDescription(gle); - return false; - } - } - } while (returnCode == FALSE); - - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = buffer.get(); - - unsigned int byteOffset = 0; - while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) { - if (ptr->Relationship == RelationNumaNode) { + for (auto&& lpi : getLogicalProcessorInformationRecords()) { + if (lpi.Relationship == RelationNumaNode) // Non-NUMA systems report a single record of this type. - numaNodeCount++; - } - - byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); - ptr++; + ++numaNodeCount; } // For non-NUMA machines, the count is 1 -- cgit v1.2.1