summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsergey.galtsev <sergey.galtsev@mongodb.com>2021-12-01 20:53:53 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-12-01 21:05:46 +0000
commit576bd6e7a64161d681e7327b70b0ba8340c61513 (patch)
tree0aaae47f5645bc5322b8828890287793934a6b00
parent31fef3c18f6fd8278b306cc9d8bc5ef93e8d2e7e (diff)
downloadmongo-576bd6e7a64161d681e7327b70b0ba8340c61513.tar.gz
SERVER-28953 Capture mount statistics in FTDC
-rw-r--r--src/mongo/db/ftdc/ftdc_system_stats_linux.cpp8
-rw-r--r--src/mongo/util/SConscript3
-rw-r--r--src/mongo/util/procparser.cpp46
-rw-r--r--src/mongo/util/procparser.h5
-rw-r--r--src/mongo/util/procparser_test.cpp154
5 files changed, 216 insertions, 0 deletions
diff --git a/src/mongo/db/ftdc/ftdc_system_stats_linux.cpp b/src/mongo/db/ftdc/ftdc_system_stats_linux.cpp
index 5e3b4064816..acb28f7bb6c 100644
--- a/src/mongo/db/ftdc/ftdc_system_stats_linux.cpp
+++ b/src/mongo/db/ftdc/ftdc_system_stats_linux.cpp
@@ -141,6 +141,14 @@ public:
}
{
+ BSONObjBuilder subObjBuilder(builder.subobjStart("mounts"_sd));
+ processStatusErrors(
+ procparser::parseProcSelfMountStatsFile("/proc/self/mountinfo"_sd, &subObjBuilder),
+ &subObjBuilder);
+ subObjBuilder.doneFast();
+ }
+
+ {
BSONObjBuilder subObjBuilder(builder.subobjStart("vmstat"_sd));
processStatusErrors(
procparser::parseProcVMStatFile("/proc/vmstat"_sd, kVMKeys, &subObjBuilder),
diff --git a/src/mongo/util/SConscript b/src/mongo/util/SConscript
index 93a9707a432..d5a379e003c 100644
--- a/src/mongo/util/SConscript
+++ b/src/mongo/util/SConscript
@@ -512,6 +512,9 @@ if env.TargetOSIs('linux'):
LIBDEPS=[
'$BUILD_DIR/mongo/base',
],
+ LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/third_party/shim_pcrecpp',
+ ],
)
if env.TargetOSIs('windows'):
diff --git a/src/mongo/util/procparser.cpp b/src/mongo/util/procparser.cpp
index 24b9d1e2c95..c79671b71d6 100644
--- a/src/mongo/util/procparser.cpp
+++ b/src/mongo/util/procparser.cpp
@@ -39,6 +39,7 @@
#include <boost/algorithm/string/split.hpp>
#include <boost/filesystem.hpp>
#include <fcntl.h>
+#include <pcrecpp.h>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
@@ -638,6 +639,51 @@ Status parseProcDiskStatsFile(StringData filename,
return parseProcDiskStats(disks, swString.getValue(), builder);
}
+Status parseProcSelfMountStatsImpl(
+ StringData data,
+ BSONObjBuilder* builder,
+ boost::filesystem::space_info (*getSpace)(const boost::filesystem::path&,
+ boost::system::error_code&)) {
+ invariant(getSpace);
+ std::istringstream iss(data.toString());
+ for (std::string line; std::getline(iss, line);) {
+ // 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)
+ static const pcrecpp::RE kRe(R"re(\d+ \d+ \d+:\d+ \S+ (\S+))re");
+ std::string mountPoint;
+ if (kRe.PartialMatch(line, &mountPoint)) {
+ boost::filesystem::path p(mountPoint);
+ boost::system::error_code ec;
+ boost::filesystem::space_info spaceInfo = getSpace(p, ec);
+ if (!ec.failed() && spaceInfo.capacity) {
+ BSONObjBuilder bob(builder->subobjStart(mountPoint));
+ bob.appendNumber("capacity", static_cast<long long>(spaceInfo.capacity));
+ bob.appendNumber("available", static_cast<long long>(spaceInfo.available));
+ bob.appendNumber("free", static_cast<long long>(spaceInfo.free));
+ bob.doneFast();
+ }
+ }
+ }
+
+ return Status::OK();
+}
+
+Status parseProcSelfMountStats(StringData data, BSONObjBuilder* builder) {
+ return parseProcSelfMountStatsImpl(data, builder, &boost::filesystem::space);
+}
+
+Status parseProcSelfMountStatsFile(StringData filename, BSONObjBuilder* builder) {
+ auto swString = readFileAsString(filename);
+ if (!swString.isOK()) {
+ return swString.getStatus();
+ }
+
+ return parseProcSelfMountStats(swString.getValue(), builder);
+}
+
namespace {
/**
diff --git a/src/mongo/util/procparser.h b/src/mongo/util/procparser.h
index 5c2d106665b..55f6c3c296f 100644
--- a/src/mongo/util/procparser.h
+++ b/src/mongo/util/procparser.h
@@ -129,6 +129,11 @@ Status parseProcDiskStatsFile(StringData filename,
BSONObjBuilder* builder);
/**
+ * Read from file, and write the used/free space data for available mounts.
+ */
+Status parseProcSelfMountStatsFile(StringData filename, BSONObjBuilder* builder);
+
+/**
* Get a vector of disks to monitor by enumerating the specified directory.
*
* If the directory does not exist, or otherwise permission is denied, returns an empty vector.
diff --git a/src/mongo/util/procparser_test.cpp b/src/mongo/util/procparser_test.cpp
index 0c1db18da91..637c7127970 100644
--- a/src/mongo/util/procparser_test.cpp
+++ b/src/mongo/util/procparser_test.cpp
@@ -43,6 +43,14 @@
namespace mongo {
+namespace procparser {
+Status parseProcSelfMountStatsImpl(StringData data,
+ BSONObjBuilder* builder,
+ boost::filesystem::space_info (*getSpace)(
+ const boost::filesystem::path&, boost::system::error_code&));
+}
+
+
namespace {
using StringMap = std::map<std::string, uint64_t>;
@@ -96,6 +104,10 @@ StringMap toNestedStringMap(BSONObj& obj) {
ASSERT_OK(procparser::parseProcDiskStats(_disks, _x, &builder)); \
auto obj = builder.obj(); \
auto stringMap = toNestedStringMap(obj);
+#define ASSERT_PARSE_MOUNTSTAT(x) \
+ BSONObjBuilder builder; \
+ ASSERT_OK(procparser::parseProcSelfMountStatsImpl(x, &builder, &mockGetSpace)); \
+ auto obj = builder.obj();
#define ASSERT_PARSE_VMSTAT(_keys, _x) \
BSONObjBuilder builder; \
ASSERT_OK(procparser::parseProcVMStat(_keys, _x, &builder)); \
@@ -654,6 +666,148 @@ TEST(FTDCProcDiskStats, TestLocalDiskStats) {
}
}
+boost::filesystem::space_info mockGetSpace(const boost::filesystem::path& p,
+ boost::system::error_code& ec) {
+ ec = boost::system::error_code();
+ boost::filesystem::space_info result;
+ if (p.string() == "/") {
+ result.available = 11213234231;
+ result.capacity = 23432543255;
+ result.free = 12387912837;
+ } else if (p.string() == "/boot") {
+ result.available = result.free = 777;
+ result.capacity = 888;
+ } else if (p.string() == "/home/ubuntu") {
+ result.available = result.free = 0;
+ result.capacity = 999;
+ } else if (p.string() == "/opt") {
+ result.available = result.free = result.capacity = 0;
+ } else if (p.string() == "/var") {
+ ec.assign(1, ec.category());
+ }
+ return result;
+}
+
+TEST(FTDCProcMountStats, TestMountStatsHappyPath) {
+ // clang-format off
+ ASSERT_PARSE_MOUNTSTAT("25 30 0:23 / /sys rw,nosuid,nodev,noexec,relatime shared:7 - sysfs sysfs rw\n"
+ "26 30 0:5 / /proc rw,nosuid,nodev,noexec,relatime shared:13 - proc proc rw\n"
+ "27 30 0:6 / /dev rw,nosuid,relatime shared:2 - devtmpfs udev rw,size=8033308k,nr_inodes=2008327,mode=755\n"
+ "28 27 0:24 / /dev/pts rw,nosuid,noexec,relatime shared:3 - devpts devpts rw,gid=5,mode=620,ptmxmode=000\n"
+ "29 30 0:25 / /run rw,nosuid,noexec,relatime shared:5 - tmpfs tmpfs rw,size=1609528k,mode=755\n"
+ "30 1 259:2 / / rw,relatime shared:1 - ext4 /dev/nvme0n1p1 rw,discard\n"
+ "31 25 0:7 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime shared:8 - securityfs securityfs rw\n"
+ "32 27 0:26 / /dev/shm rw,nosuid,nodev shared:4 - tmpfs tmpfs rw\n"
+ "33 29 0:27 / /run/lock rw,nosuid,nodev,noexec,relatime shared:6 - tmpfs tmpfs rw,size=5120k\n"
+ "34 25 0:28 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:9 - tmpfs tmpfs ro,mode=755\n"
+ "35 34 0:29 / /sys/fs/cgroup/unified rw,nosuid,nodev,noexec,relatime shared:10 - cgroup2 cgroup rw\n"
+ "36 34 0:30 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:11 - cgroup cgroup rw,xattr,name=systemd\n"
+ "37 25 0:31 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime shared:12 - pstore pstore rw\n"
+ "38 34 0:32 / /sys/fs/cgroup/rdma rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,rdma\n"
+ "39 34 0:33 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,net_cls,net_prio\n"
+ "40 34 0:34 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,perf_event\n"
+ "41 34 0:35 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,cpu,cpuacct\n"
+ "42 34 0:36 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,freezer\n"
+ "43 34 0:37 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,cpuset\n"
+ "44 34 0:38 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:20 - cgroup cgroup rw,devices\n"
+ "45 34 0:39 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:21 - cgroup cgroup rw,memory\n"
+ "46 34 0:40 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:22 - cgroup cgroup rw,pids\n"
+ "47 34 0:41 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:23 - cgroup cgroup rw,blkio\n"
+ "48 34 0:42 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:24 - cgroup cgroup rw,hugetlb\n"
+ "49 27 0:21 / /dev/mqueue rw,relatime shared:25 - mqueue mqueue rw\n"
+ "50 26 0:43 / /proc/sys/fs/binfmt_misc rw,relatime shared:26 - autofs systemd-1 rw,fd=32,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=3379\n"
+ "51 27 0:44 / /dev/hugepages rw,relatime shared:27 - hugetlbfs hugetlbfs rw,pagesize=2M\n"
+ "52 29 0:45 / /run/rpc_pipefs rw,relatime shared:28 - rpc_pipefs sunrpc rw\n"
+ "53 25 0:8 / /sys/kernel/debug rw,relatime shared:29 - debugfs debugfs rw\n"
+ "54 25 0:46 / /sys/fs/fuse/connections rw,relatime shared:30 - fusectl fusectl rw\n"
+ "55 25 0:22 / /sys/kernel/config rw,relatime shared:31 - configfs configfs rw\n"
+ "90 30 7:2 / /snap/core18/2128 ro,nodev,relatime shared:33 - squashfs /dev/loop2 ro\n"
+ "94 30 259:0 / /home/ubuntu rw,noatime shared:35 - xfs /dev/nvme1n1 rw,attr2,inode64,logbufs=8,logbsize=32k,noquota\n"
+ "96 50 0:47 / /proc/sys/fs/binfmt_misc rw,relatime shared:36 - binfmt_misc binfmt_misc rw\n"
+ "192 30 7:3 / /snap/amazon-ssm-agent/3552 ro,nodev,relatime shared:38 - squashfs /dev/loop3 ro\n"
+ "196 30 7:5 / /snap/amazon-ssm-agent/4046 ro,nodev,relatime shared:39 - squashfs /dev/loop5 ro\n"
+ "435 30 0:53 / /var/lib/lxcfs rw,nosuid,nodev,relatime shared:248 - fuse.lxcfs lxcfs rw,user_id=0,group_id=0,allow_other\n"
+ "379 30 7:6 / /snap/snapd/13270 ro,nodev,relatime shared:194 - squashfs /dev/loop6 ro\n"
+ "387 30 7:4 / /snap/snapd/13640 ro,nodev,relatime shared:198 - squashfs /dev/loop4 ro\n"
+ "92 30 7:1 / /snap/core18/2246 ro,nodev,relatime shared:34 - squashfs /dev/loop1 ro\n"
+ "88 29 0:51 / /run/user/1000 rw,nosuid,nodev,relatime shared:32 - tmpfs tmpfs rw,size=1609524k,mode=700,uid=1000,gid=1000\n");
+ // clang-format on
+ ASSERT(obj["/"]["capacity"].isNumber());
+ ASSERT(obj["/"]["capacity"].number() == 23432543255);
+ ASSERT(obj["/"]["available"].isNumber());
+ ASSERT(obj["/"]["available"].number() == 11213234231);
+ ASSERT(obj["/"]["free"].isNumber());
+ ASSERT(obj["/"]["free"].number() == 12387912837);
+ ASSERT(obj["/home/ubuntu"]["capacity"].isNumber());
+ ASSERT(obj["/home/ubuntu"]["capacity"].number() == 999);
+ ASSERT(obj["/home/ubuntu"]["available"].isNumber());
+ ASSERT(obj["/home/ubuntu"]["available"].number() == 0);
+ ASSERT(obj["/home/ubuntu"]["free"].isNumber());
+ ASSERT(obj["/home/ubuntu"]["free"].number() == 0);
+}
+
+TEST(FTDCProcMountStats, TestMountStatsZeroCapacity) {
+ // clang-format off
+ ASSERT_PARSE_MOUNTSTAT("25 30 0:23 / /sys rw,nosuid,nodev,noexec,relatime shared:7 - sysfs sysfs rw\n"
+ "26 30 0:5 / /proc rw,nosuid,nodev,noexec,relatime shared:13 - proc proc rw\n"
+ "27 30 0:6 / /dev rw,nosuid,relatime shared:2 - devtmpfs udev rw,size=8033308k,nr_inodes=2008327,mode=755\n"
+ "28 27 0:24 / /dev/pts rw,nosuid,noexec,relatime shared:3 - devpts devpts rw,gid=5,mode=620,ptmxmode=000\n"
+ "29 30 0:25 / /run rw,nosuid,noexec,relatime shared:5 - tmpfs tmpfs rw,size=1609528k,mode=755\n"
+ "30 1 259:2 / / rw,relatime shared:1 - ext4 /dev/nvme0n1p1 rw,discard\n"
+ "31 25 0:7 / /opt rw,nosuid,nodev,noexec,relatime shared:8 - ext4 /dev/nvme0n1p2 rw,discard\n"
+ "88 29 0:51 / /run/user/1000 rw,nosuid,nodev,relatime shared:32 - tmpfs tmpfs rw,size=1609524k,mode=700,uid=1000,gid=1000\n");
+ // clang-format on
+ ASSERT(!obj.hasElement("/opt"));
+}
+
+TEST(FTDCProcMountStats, TestMountStatsError) {
+ // clang-format off
+ ASSERT_PARSE_MOUNTSTAT("25 30 0:23 / /sys rw,nosuid,nodev,noexec,relatime shared:7 - sysfs sysfs rw\n"
+ "26 30 0:5 / /proc rw,nosuid,nodev,noexec,relatime shared:13 - proc proc rw\n"
+ "27 30 0:6 / /dev rw,nosuid,relatime shared:2 - devtmpfs udev rw,size=8033308k,nr_inodes=2008327,mode=755\n"
+ "28 27 0:24 / /dev/pts rw,nosuid,noexec,relatime shared:3 - devpts devpts rw,gid=5,mode=620,ptmxmode=000\n"
+ "29 30 0:25 / /run rw,nosuid,noexec,relatime shared:5 - tmpfs tmpfs rw,size=1609528k,mode=755\n"
+ "30 1 259:2 / / rw,relatime shared:1 - ext4 /dev/nvme0n1p1 rw,discard\n"
+ "31 25 0:7 / /var rw,nosuid,nodev,noexec,relatime shared:8 - ext4 /dev/nvme0n1p2 rw,discard\n"
+ "88 29 0:51 / /run/user/1000 rw,nosuid,nodev,relatime shared:32 - tmpfs tmpfs rw,size=1609524k,mode=700,uid=1000,gid=1000\n");
+ // clang-format on
+ ASSERT(!obj.hasElement("/var"));
+}
+
+TEST(FTDCProcMountStats, TestMountStatsGarbageInput) {
+ ASSERT_PARSE_MOUNTSTAT(
+ "sadjlkyfgs odyfg\x01$fgeairsufg oireasfgrysudvfbg \n\n\t\t\t34756gusf\r342");
+}
+
+TEST(FTDCProcMountStats, TestMountStatsSomewhatGarbageInput) {
+ // clang-format off
+ ASSERT_PARSE_MOUNTSTAT("11 11 11 11 11 11 11 11 11 11 11 11 11\n"
+ "\n"
+ " \n"
+ "asidhsif gsys gfuwe esuf usfg 755\n"
+ "28 27 754\t\x01\n"
+ "29 30 0:25 / /run sdfget3 354t89 re89y3q9t q9ty fg\n"
+ "30 1 259:2 / / rw,relatime shared:1 - ext4 /dev/nvme0n1p1 rw,discard\n"
+ "31 25 0:7 / /boot rw,nosuid,nodev,noexec,relatime shared:8 - ext4 /dev/nvme0n1p2 rw,discard\n"
+ "88 29 0:51 / /run/user/1000 rw,nosuid,nodev,relatime shared:32 - tmpfs tmpfs rw,size=1609524k,mode=700,uid=1000,gid=1000");
+ // clang-format on
+ ASSERT(obj.hasElement("/boot"));
+ ASSERT(obj["/boot"]["capacity"].isNumber());
+ ASSERT(obj["/boot"]["capacity"].number() == 888);
+ ASSERT(obj["/boot"]["available"].isNumber());
+ ASSERT(obj["/boot"]["available"].number() == 777);
+ ASSERT(obj["/boot"]["free"].isNumber());
+ ASSERT(obj["/boot"]["free"].number() == 777);
+}
+
+// Test we can parse the /proc/self/mountinfo on this machine.
+// This tests is designed to exercise our parsing code on various Linuxes and never fail
+TEST(FTDCProcMountStats, TestLocalMountStats) {
+ BSONObjBuilder bb;
+ ASSERT_OK(procparser::parseProcSelfMountStatsFile("/proc/self/mountinfo", &bb));
+ auto obj = bb.obj();
+ ASSERT(obj.hasElement("/"));
+}
TEST(FTDCProcVMStat, TestVMStat) {