summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Russell <gabriel.russell@mongodb.com>2020-08-26 11:42:31 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-10-29 15:20:02 +0000
commit61eb18dc3dfa78a3501e0ea27fc9168a254196e7 (patch)
treeb726502e19c9baaa54f610bc6e11d884dcd0f36e
parentc6635ba1c55a508eeb35e82277c732d4caa9cea9 (diff)
downloadmongo-61eb18dc3dfa78a3501e0ea27fc9168a254196e7.tar.gz
SERVER-50123 Record number of physical cores on all platforms
-rw-r--r--src/mongo/util/processinfo.h11
-rw-r--r--src/mongo/util/processinfo_linux.cpp185
-rw-r--r--src/mongo/util/processinfo_windows.cpp102
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,13 +101,20 @@ 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 <iostream>
#include <malloc.h>
+#include <pcrecpp.h>
#include <sched.h>
#include <stdio.h>
#include <sys/mman.h>
@@ -54,6 +55,7 @@
#include <boost/filesystem.hpp>
#include <boost/none.hpp>
#include <boost/optional.hpp>
+#include <pcrecpp.h>
#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<void(const std::string&)> f;
+ };
+ std::vector<LineProcessor> lineProcessors;
+ std::function<void()> 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<CpuId, decltype(cmp)> 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<long long>(pageSize));
bExtra.append("numPages", static_cast<int>(sysconf(_SC_PHYS_PAGES)));
bExtra.append("maxOpenFiles", static_cast<int>(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<sizeof(Slpi)>;
+
+struct LpiRecords {
+ const Slpi* begin() const {
+ return reinterpret_cast<const Slpi*>(slpiRecords.get());
+ }
+
+ const Slpi* end() const {
+ return begin() + count;
+ }
+
+ std::unique_ptr<SlpiBuf[]> 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<Slpi*>(lpiRecords.slpiRecords.get()), &returnLength);
+ if (returnCode == FALSE) {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ lpiRecords.slpiRecords = std::unique_ptr<SlpiBuf[]>(
+ 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<unsigned long long>(ntsysinfo.dwPageSize);
bExtra.append("pageSize", static_cast<long long>(pageSize));
+ bExtra.append("physicalCores", static_cast<int>(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<SYSTEM_LOGICAL_PROCESSOR_INFORMATION[]> buffer;
-
- LPFN_GLPI glpi(reinterpret_cast<LPFN_GLPI>(
- 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<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION>(
- 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